ZOJ 2112 Dynamic Rankings [树状数组套主席树 || CDQ分治&整体二分]

来源:互联网 发布:假面骑士wizard知乎 编辑:程序博客网 时间:2024/05/31 15:18

题意:给出长度为n的数组,有两种操作

1、询问区间【L,R】第k大的数的权值

2、将第i个数修改为t

题解:

①第一种方法是树状数组套主席树,要建立关于原数组的主席树和关于修改的树状数组套主席树,两个主席树都以权值建树,由于某个点改变所以影响到很多区间,而树状数组的每一个点都可以代表一个区间,所以我们通过树状数组logn查找相应需要修改的所有的区间,再通过logn来修改相应区间上点的权值,若每次修改都在树状数组上面新建一颗线段树则会m*n*logn爆空间,所以我们通过主席树动态加点,所以询问操作的总共需要保存的空间为m*logn,然后关于原数组的主席树空间为n*logn,所以总的

空间复杂度为(n+m)*logn,然后由于新建一个关于原数组的主席树的时间复杂度为n*logn,修改操作总的复杂度为m*logn*logn所以总的时间复杂度为m*logn*logn。

树状数组套主席树AC代码:

#include<stdio.h>#include<string.h>#include<algorithm>#include<map>using namespace std;#define N 50005int n,m;int a[N],la[N];int b[N*2],top;int q[N][4];map<int,int>mp;int tree[N*40],lchild[N*40],rchild[N*40];int root[N],tot;int c[N],use[N];void update(int last,int cur,int L,int R,int x,int k){tree[cur]=tree[last]+k;lchild[cur]=lchild[last];rchild[cur]=rchild[last];if(L==R)return ;int mid=L+R>>1;if(x<=mid)update(lchild[last],lchild[cur]=++tot,L,mid,x,k);else update(rchild[last],rchild[cur]=++tot,mid+1,R,x,k);}int lowbit(int i){return i&(-i);}void add(int i,int v,int k){while(i<=n){int last=c[i];update(last,c[i]=++tot,1,top,mp[v],k);i+=lowbit(i);}}int sum(int i){int ans=0;while(i){ans+=tree[lchild[use[i]]];i-=lowbit(i);}return ans;}int query(int l,int r,int k){for(int i=l-1;i;i-=lowbit(i))use[i]=c[i];for(int i=r;i;i-=lowbit(i))use[i]=c[i];int L=1,R=top;int last=root[l-1],cur=root[r];while(1){if(L==R)return b[L-1];int mid=L+R>>1;int lsum=tree[lchild[cur]]-tree[lchild[last]]+sum(r)-sum(l-1);if(lsum>=k){R=mid;for(int i=l-1;i;i-=lowbit(i))use[i]=lchild[use[i]];for(int i=r;i;i-=lowbit(i))use[i]=lchild[use[i]];last=lchild[last];cur=lchild[cur];}else{k-=lsum;L=mid+1;for(int i=l-1;i;i-=lowbit(i))use[i]=rchild[use[i]];for(int i=r;i;i-=lowbit(i))use[i]=rchild[use[i]];last=rchild[last];cur=rchild[cur];}}}int main(){int T;scanf("%d",&T);while(T--){tot=top=0;mp.clear();memset(c,0,sizeof(c));scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&a[i]);b[top++]=a[i];la[i]=a[i];}for(int i=0;i<m;i++){char op[2];scanf("%s",op);if(op[0]=='Q'){int l,r,k;scanf("%d%d%d",&l,&r,&k);q[i][0]=0;q[i][1]=l;q[i][2]=r;q[i][3]=k;}else{int pos,w;scanf("%d%d",&pos,&w);q[i][0]=1;q[i][1]=pos;q[i][2]=w;b[top++]=w;}}sort(b,b+top);top=unique(b,b+top)-b;for(int i=0;i<top;i++)mp[b[i]]=i+1;for(int i=1;i<=n;i++)update(root[i-1],root[i]=++tot,1,top,mp[a[i]],1);for(int i=0;i<m;i++){if(q[i][0]){add(q[i][1],la[q[i][1]],-1);add(q[i][1],q[i][2],1);la[q[i][1]]=q[i][2];}else printf("%d\n",query(q[i][1],q[i][2],q[i][3]));}}}

②第二种方法是CDQ分治中的整体二分

每次关于结果,也就是第k个权值为多少进行二分,通过树状数组维护每一次二分时权值小于x的点的个数,将询问分为两种,权值小于x的总点数小于等于k,第二种是总数大于k,然后通过归并排序,分为权值满足【L,mid】的部分和【mid+1,R】的部分继续递归下去。

由于CDQ分治总共logn次递归,每次递归查询和修改的总复杂度为n*logn所以总的时间复杂度为n*logn*logn,空间复杂度为nlogn。


整体二分AC代码:

#include<stdio.h>#include<algorithm>#include<map>#include<string.h>#define N 50005using namespace std;struct node{int op,l,pos,k;int t;int add;int id;}a[N*2],newa[N*2];int t,top,tot;int b[N*2];int pos[N];int c[N];int cum[N],mark[N],ANS[N];map<int,int>mp;int lowbit(int i){return i&(-i);}void change(int i,int k){while(i<=top+1){c[i]+=k;i+=lowbit(i);}}int sum(int i){int ans=0;while(i){ans+=c[i];i-=lowbit(i);}return ans;}void solve(int posl,int posr,int l,int r){if(l==r){for(int i=posl;i<=posr;i++)if(a[i].op==3)ANS[a[i].id]=l;return ;}int mid=(l+r)/2;for(int i=posl;i<=posr;i++){if(a[i].op==1&&a[i].add<=mid)change(a[i].pos,1);if(a[i].op==2&&a[i].add<=mid)change(a[i].pos,-1);if(a[i].op==3){mark[a[i].id]=sum(a[i].pos)-sum(a[i].l-1);cum[a[i].id]+=mark[a[i].id];}}for(int i=posl;i<=posr;i++){if(a[i].op==1&&a[i].add<=mid)change(a[i].pos,-1);if(a[i].op==2&&a[i].add<=mid)change(a[i].pos,1);}int l1=posl;for(int i=posl;i<=posr;i++){if((a[i].op==1||a[i].op==2)&&a[i].add<=mid)newa[l1++]=a[i];else if(a[i].op==3&&cum[a[i].id]>=a[i].k)newa[l1++]=a[i];}int l2=l1;for(int i=posl;i<=posr;i++){if((a[i].op==1||a[i].op==2)&&a[i].add>mid)newa[l2++]=a[i];else if(a[i].op==3&&cum[a[i].id]<a[i].k){mark[a[i].id]=0;newa[l2++]=a[i];}}for(int i=posl;i<=posr;i++){a[i]=newa[i];if(a[i].op==3)cum[a[i].id]-=mark[a[i].id];}solve(posl,l1-1,l,mid);solve(l1,posr,mid+1,r);}void init(){tot=top=t=0;memset(ANS,-1,sizeof(ANS));memset(cum,0,sizeof(cum));}int main(){int T;scanf("%d",&T);while(T--){init();mp.clear();int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int k;scanf("%d",&k);a[++tot].op=1;a[tot].pos=i;a[tot].t=++t;a[tot].add=k;b[top++]=k;pos[i]=k;}for(int i=1;i<=m;i++){char op[2];scanf("%s",op);if(op[0]=='Q'){int l,r,k;scanf("%d%d%d",&l,&r,&k);a[++tot].op=3;a[tot].l=l;a[tot].pos=r;a[tot].k=k;a[tot].t=++t;a[tot].id=i;}else{int r,val;scanf("%d%d",&r,&val);a[++tot].op=2;a[tot].pos=r;a[tot].t=++t;a[tot].add=pos[r];a[++tot].op=1;a[tot].pos=r;a[tot].t=++t;a[tot].add=val;b[top++]=val;pos[r]=val;}}sort(b,b+top);top=unique(b,b+top)-b;for(int i=0;i<top;i++)mp[b[i]]=i+1;for(int i=1;i<=tot;i++)if(a[i].op!=3)a[i].add=mp[a[i].add];solve(1,t,1,top+1);for(int i=1;i<=m;i++)if(ANS[i]!=-1)printf("%d\n",b[ANS[i]-1]);}}


整体二分AC代码:


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