ZOJ2112 Dynamic Ranking (主席树+树状数组)

来源:互联网 发布:免费开淘宝店 编辑:程序博客网 时间:2024/06/11 00:19


/*给定n个数,可以动态修改某个数,随时查询某个区间第k大的值。http://blog.csdn.net/regina8023/article/details/41911837树状数组套主席树。*/#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#define maxn 10005using namespace std;int use[maxn*10],n,m,size,tot=0,all=0,h[maxn*10],v[maxn*10],t[maxn*10];struct chairtree{int l,r,size;//size保存[l,r]区间数据的个数 }a[maxn*300];//记录输入的m条指令 struct question{int l,r,k;}q[maxn];//树状数组 int lowbit(int x){return x&(-x);}//对数组h排序,并求出有多少个不同的值 void Hash1(){sort(h+1,h+1+all);size=unique(h+1,h+1+all)-h-1;}//求出x在去重后的数据中的序号 int Hash(int x){return lower_bound(h+1,h+1+size,x)-h;}//建线段树 int Build(int l,int r){int now=++tot;a[now].size=0;if (l==r) return now;int m=(l+r)>>1;a[now].l=Build(l,m);a[now].r=Build(m+1,r);return now;}//更新。每更新一个节点相当于建立新的线段树 //非递归更新 //val为1或-1.表示删除数据或新增数据//修改数据:要先删除然后再增加 int Update(int root,int p,int val){int now=++tot,tmp=now;int l=1,r=size;a[now].size=a[root].size+val;//now是新节点 while (l<r) //非递归更新 {int m=(l+r)>>1;if (p<=m)//左子树新增节点 {a[now].l=++tot;a[now].r=a[root].r;root=a[root].l;now=a[now].l;r=m;}else//右子树新增节点 {a[now].l=a[root].l;a[now].r=++tot;root=a[root].r;now=a[now].r;l=m+1;}a[now].size=a[root].size+val;}return tmp;}//把第x个数改成p(p已经离散化为去重后的序号)//val为1或-1 void Add(int x,int p,int val){printf("Add:%d ->%d %d  Change:",x,p,val);for (int i=x;i<=n;i+=lowbit(i))    //树状数组只需修改logn个{t[i]=Update(t[i],p,val);printf("%d ",i);}printf("\n");}//统计与x相关的树状数组对应的主席树的左子树个数//因为要找第k大的元素,只计算左边就可以知道第k大的数是在左还是在右 int Getsum(int x){int ans=0;for (int i=x;i;i-=lowbit(i))ans+=a[a[use[i]].l].size;return ans;}//查询区间 [lx,rx]的第k个数 int Query(int lx,int rx,int k){int l=1,r=size;//求出rx,lx-1对应的树状数组+主席树 for (int i=lx-1;i;i-=lowbit(i)) use[i]=t[i];for (int i=rx;i;i-=lowbit(i)) use[i]=t[i];while (l<r){int m=(l+r)>>1;int tmp=Getsum(rx)-Getsum(lx-1);if (tmp>=k)//第k大的数在左子树,往左子树走 {for (int i=lx-1;i;i-=lowbit(i)) use[i]=a[use[i]].l;        for (int i=rx;i;i-=lowbit(i)) use[i]=a[use[i]].l;        r=m;}else//往右子树走 {for (int i=lx-1;i;i-=lowbit(i))use[i]=a[use[i]].r;for (int i=rx;i;i-=lowbit(i))use[i]=a[use[i]].r;k-=tmp;l=m+1;}}return l;}int main(){    freopen("a.txt","r",stdin);    scanf("%d%d",&n,&m);for (int i=1;i<=n;i++)scanf("%d",&v[i]),h[i]=v[i];all=n;for (int i=1;i<=m;i++){char str[10];int l,r,k;scanf("%s",str);if (str[0]=='Q'){scanf("%d%d%d",&l,&r,&k);//查询[l,r]区间第k大的数 q[i].l=l,q[i].r=r,q[i].k=k;}else{scanf("%d%d",&r,&k);//把第r个数改为k q[i].l=0,q[i].r=r,q[i].k=k;            h[++all]=k;}}//h数组记录了全部数据(包括后面要修改的数据)Hash1();   //去掉重复的数据,size为不相同的数据个数 //对每个数据建立一棵树(主席树) t[0]=Build(1,size);for (int i=1;i<=n;i++)t[i]=t[0];//按顺序把原数据增加到对应的主席树//同时修改多棵树 lowbit(x) for (int i=1;i<=n;i++)Add(i,Hash(v[i]),1);//处理给出的指令 for (int i=1;i<=m;i++){if (q[i].l){printf("%d\n",h[Query(q[i].l,q[i].r,q[i].k)]);}else{Add(q[i].r,Hash(v[q[i].r]),-1); //先把原数去掉 Add(q[i].r,Hash(q[i].k),1);//增加新数 v[q[i].r]=q[i].k;//v数组保存最新的数据 }}return 0;}


阅读全文
0 0
原创粉丝点击