POJ2104 hdu2665 主席树入门 Kth-number

来源:互联网 发布:apache启动报错 编辑:程序博客网 时间:2024/06/06 08:55
—网易游戏雷火盘古校园招聘开始!

Kth number

Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 12438    Accepted Submission(s): 3788


Problem Description
Give you a sequence and ask you the kth big number of a inteval.
 

Input
The first line is the number of the test cases. 
For each test case, the first line contain two integer n and m (n, m <= 100000), indicates the number of integers in the sequence and the number of the quaere. 
The second line contains n integers, describe the sequence. 
Each of following m lines contains three integers s, t, k. 
[s, t] indicates the interval and k indicates the kth big number in interval [s, t]
 

Output
For each test case, output m lines. Each line contains the kth big number.
 

Sample Input
1 10 1 1 4 2 3 5 6 7 8 9 0 1 3 2
 

Sample Output
2
 

Source
HDU男生专场公开赛——赶在女生之前先过节(From WHU)
两题都是一样的告诉你一个数列然后m次询问,让你说出第k大的数。嗯。。是主席树,也就是函数式线段树或者说可持久化线段树啊。这他么什么东西啊。。一开始学。。思想很简单 但是代码实现思考了很久特难受的一天。可持久化的意思就是保留了历史版本,每一次新的操作比如修改或者增加删除就会新增一颗线段树即一个新的版本,然后我们查询的时候就是对这些所有的历史版本进行抓取我们想要的信息。嗯听听还是不难的那么我们怎么实现呢。
分析这道题,给定n个数,那么我们可以这样建立线段树,比如第i棵线段树保留的是从第1个数到第i个数的信息,也就是他们的前缀内容。因此,n个数就有n个版本的线段树,每次新加入一个节点,我们就需要用一个新的线段树指向上一个版本的线段树,然后在相应的区间(即节点)上进行节点的修改,比如统计个数的变量+1,或者新增一个节点记录该值。
HDU这题没说输入数据多大,poj那题说了1e9 我因此就当做需要离散化,因此我们先离散化然后建立n个线段树。
可以看一下我画的主席树的图,没有画出的部分就是不用修改的与上一个版本的内容相同。


#include<iostream>#include<vector>#include<algorithm>#include<cstdio>#include<cstring>using namespace std;const int maxn = 1e5+5;struct node{int l,r,sum;node(){sum=0;} }T[maxn*20];//root[i] 表示的是从1 到 i 的线段树的计入内容 相当于计入的前缀的内容 int n,m,cnt,root[maxn],a[maxn],x,y,k;vector<int>v;void update(int l,int r,int &x,int y,int pos ){//注意x是引用关键在于&x的使用 即新节点加进来是要记录它得到的当前第几个节点的下标值 //cnt记录当前新建的线段树指向的原先的线段树 以及 线段树中新的修改如果不变的指向原先的部分 //sum 用来统计当前节点下方的个数T[++cnt]=T[y],T[cnt].sum++,x=cnt;//这一行就表示增加的内容 如果原先没有修改或者该节点是空的那么就要在新的线段树中增加新的节点表示新版本增加的内容 //printf("%d %d\n",x,y); if(l==r)return;int mid = (l+r)>>1;if(mid>=pos)update(l,mid,T[x].l,T[y].l,pos);//去建立T[x]的左子树 else update(mid+1,r,T[x].r,T[y].r,pos);} int query(int l, int r,int x,int y,int k){//x y 代表节点的标号 if(l==r)return l;int mid = (l+r)>>1;int sum = T[T[y].l].sum-T[T[x].l].sum;//统计左边的个数 if(sum>=k)return query(l,mid,T[x].l,T[y].l,k);else query(mid+1,r,T[x].r,T[y].r,k-sum);}int getid(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;//离散化的操作 }int main(){int t;scanf("%d",&t);while(t--){scanf("%d%d",&n,&m); v.clear();memset(T,0,sizeof(T));memset(root,0,sizeof(root));cnt = 0;for(int i=1;i<=n;i++){scanf("%d",&a[i]);v.push_back(a[i]);}sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end());for(int i=1;i<=n;i++){update(1,n,root[i],root[i-1],getid(a[i]));//每一次的加点操作都是增加一个线段树 root就是每个线段树的根节点 }for(int i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&k);printf("%d\n",v[query(1,n,root[x-1],root[y],k)-1]);//用第y个版本的时候与x-1版本的时候做减法 就可以得到他们中间的内容了 }}}


原创粉丝点击