洛谷 P2617 Dynamic Rankings

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 P2617 Dynamic Rankings相关的知识,希望对你有一定的参考价值。

写的让人看不懂,仅留作笔记

静态主席树,相当于前缀和套(可持久化方法构建的)值域线段树。

建树方法:记录前缀和的各位置的线段树的root。先建一个"第0棵线段树",是完整的(不需要用可持久化的方法),所有数据为0。后面每一个位置的前缀和放的线段树都先设root与前一位置的线段树一样,然后再按照原序列在指定位置进行(可持久化的)单点加法。(类比放数值的前缀和,每一位置的前缀和是前一位置前缀和加上当前位置数值)

带修改主席树,相当于区间树状数组套(可持久化方法构建的)值域线段树。

建树方法:记录树状数组各位置线段树的root。先建一个"第0棵线段树",是完整的,按普通线段树建,所有数据为0。一开始设所有root都为第0棵线段树的根。对于原序列某位置的值,相当于在树状数组某位置进行一次单点加法。

可持久化方法建线段树:每一个节点进行操作时,都先把原来的节点复制一份(指同时复制左右孩子(以前错在只复制了一个孩子)位置和数据,但不递归复制左右孩子),再进行更新等操作。这样的话每一次单点更新操作只会新建logn个节点(因为每次单点更新只会路过这么多点),(相比一般的树套树:外层树每一个点放一个完整的内层树)可以节省空间。

由于是值域线段树,都需要做离散化。

这么做,就可以:在log^2n的时间内,完成一次查询"某区间内小于等于某数的数的个数"的操作。(对于区间[l,r],树状数组查询出[1,r]中答案和[1,l-1]中答案,然后相减)

区间第k小:

1.二分答案,log^3n

技术分享图片
  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<tr1/unordered_map>
  4 #define lowbit(x) ((x)&(-x))
  5 #define mid ((l+r)>>1)
  6 using namespace std;
  7 using namespace tr1;
  8 unordered_map<int,int> ma;
  9 int ma2[20010];
 10 struct Q
 11 {
 12     int t,i,j,x;
 13 }q[10010];
 14 int n,m,totn,a[10010],t[20010];
 15 char tmp[10];
 16 int mem,root[20100];
 17 int L,x;
 18 int lc[3000100],rc[3000100],dat[3000100];
 19 void build(int l,int r,int& num)
 20 {
 21     num=mem++;
 22     if(l==r){/*dat[num]=0;*/return;}
 23     build(l,mid,lc[num]);
 24     build(mid+1,r,rc[num]);
 25 }
 26 void addx(int l,int r,int& num)
 27 {
 28     int t=num;num=mem++;
 29     lc[num]=lc[t];rc[num]=rc[t];
 30     if(l==r)
 31     {
 32         dat[num]=dat[t]+x;
 33         return;
 34     }
 35     if(L<=mid)    addx(l,mid,lc[num]);
 36     else    addx(mid+1,r,rc[num]);
 37     dat[num]=dat[lc[num]]+dat[rc[num]];
 38 }
 39 int query(int l,int r,int num)//返回该棵线段树中[1,x]的和,即小于等于x的数的个数
 40 {
 41     if(l==r)    return dat[num];
 42     if(x<=mid)    return query(l,mid,lc[num]);
 43     else    return dat[lc[num]]+query(mid+1,r,rc[num]);
 44 }
 45 void add(int x){while(x<=n){addx(1,totn,root[x]);x+=lowbit(x);}}
 46 int sum1(int x){int ans=0;while(x>0){ans+=query(1,totn,root[x]);x-=lowbit(x);}return ans;}
 47 int sum(int l,int r){return sum1(r)-sum1(l-1);}
 48 //返回给定x的情况下,[l,r]内小于等于x的数的个数
 49 int main()
 50 {
 51     int i,l,r;
 52     scanf("%d%d",&n,&m);
 53     for(i=1;i<=n;i++)
 54     {
 55         scanf("%d",&a[i]);
 56         t[++t[0]]=a[i];
 57     }
 58     for(i=1;i<=m;i++)
 59     {
 60         scanf("%s",tmp);
 61         if(tmp[0]==Q)
 62         {
 63             scanf("%d%d%d",&q[i].i,&q[i].j,&q[i].x);
 64             //q[i].t=0;
 65         }
 66         else if(tmp[0]==C)
 67         {
 68             scanf("%d%d",&q[i].i,&q[i].x);
 69             q[i].t=1;
 70             t[++t[0]]=q[i].x;
 71         }
 72     }
 73     sort(t+1,t+t[0]+1);
 74     //for(i=1;i<=t[0];i++)    printf("%d ",t[i]);puts("");
 75     totn=unique(t+1,t+t[0]+1)-t-1;//printf("%da\n",totn);
 76     for(i=1;i<=totn;i++)    ma[t[i]]=i,ma2[i]=t[i];
 77     for(i=1;i<=n;i++)    a[i]=ma[a[i]];//printf("%d ",a[i]);puts("");
 78     for(i=1;i<=m;i++)
 79     {
 80         if(q[i].t==1)
 81         {
 82             q[i].x=ma[q[i].x];
 83             //sz[q[i].x]++;
 84         }
 85     }
 86     build(1,totn,root[0]);
 87     //for(i=1;i<=n;i++)    root[i]=root[0];//dat[i]=dat[0];
 88     for(i=1;i<=n;i++)    L=a[i],x=1,add(i);
 89     //x=3;printf("%db\n",sum1(4));
 90     for(i=1;i<=m;i++)
 91     {
 92         if(q[i].t==0)
 93         {
 94             l=0;r=totn;
 95             while(r-l>1)
 96             {
 97                 x=(l+r)>>1;
 98                 if(sum(q[i].i,q[i].j)>=q[i].x)    r=x;
 99                 else    l=x;
100             }
101             printf("%d\n",ma2[r]);
102         }
103         else if(q[i].t==1)
104         {
105             l=0;r=totn;
106             while(r-l>1)
107             {
108                 x=(l+r)>>1;
109                 if(sum(q[i].i,q[i].i)>=1)    r=x;
110                 else    l=x;
111             }
112             L=r;x=-1;add(q[i].i);
113             L=q[i].x;x=1;add(q[i].i);
114         }
115     }
116     return 0;
117 }
View Code

2.直接在线段树上二分,log^2n。可以发现普通的在(值域)线段树上直接二分的做法这里并不能使用,因为外层树是树状数组而不是线段树。需要用(看起来比较奇怪(?)的)写法。

我的方法是:根据树状数组查询[l,r]中"小于等于某数的数的个数"的做法,先提取出与此区间相关的线段树的根节点。那么该区间内小于等于某数的树的个数,就由这些线段树中统计出的一部分的答案相加再减去一部分的答案。而这些线段树的结构是完全一致的,因此可以同步地让这些节点向左/右儿子走。

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<tr1/unordered_map>
  4 #define lowbit(x) ((x)&(-x))
  5 #define mid ((l+r)>>1)
  6 using namespace std;
  7 using namespace tr1;
  8 unordered_map<int,int> ma;
  9 int ma2[20010];
 10 struct Q
 11 {
 12     int t,i,j,x;
 13 }q[10010];
 14 int n,m,totn,a[10010],t[20010];
 15 char tmp[10];
 16 int mem,root[20100];
 17 int L,x;
 18 int lc[3000100],rc[3000100],dat[3000100];
 19 void build(int l,int r,int& num)
 20 {
 21     num=mem++;
 22     if(l==r){/*dat[num]=0;*/return;}
 23     build(l,mid,lc[num]);
 24     build(mid+1,r,rc[num]);
 25 }
 26 void addx(int l,int r,int& num)
 27 {
 28     int t=num;num=mem++;
 29     lc[num]=lc[t];rc[num]=rc[t];
 30     if(l==r)
 31     {
 32         dat[num]=dat[t]+x;
 33         return;
 34     }
 35     if(L<=mid)    addx(l,mid,lc[num]);
 36     else    addx(mid+1,r,rc[num]);
 37     dat[num]=dat[lc[num]]+dat[rc[num]];
 38 }
 39 void add(int x){while(x<=n){addx(1,totn,root[x]);x+=lowbit(x);}}
 40 int tmpr[2][20];
 41 int query(int L,int R,int k)//返回[L,R]内第k小的数
 42 {
 43     int tx,l=1,r=totn,now,i;
 44     for(tx=R,tmpr[0][0]=0;tx>0;tx-=lowbit(tx))    tmpr[0][++tmpr[0][0]]=root[tx];
 45     for(tx=L-1,tmpr[1][0]=0;tx>0;tx-=lowbit(tx))    tmpr[1][++tmpr[1][0]]=root[tx];
 46     while(l!=r)
 47     {
 48         now=0;
 49         for(i=1;i<=tmpr[0][0];i++)    now+=dat[lc[tmpr[0][i]]];
 50         for(i=1;i<=tmpr[1][0];i++)    now-=dat[lc[tmpr[1][i]]];
 51         if(now>=k)
 52         {
 53             r=mid;
 54             for(i=1;i<=tmpr[0][0];i++)    tmpr[0][i]=lc[tmpr[0][i]];
 55             for(i=1;i<=tmpr[1][0];i++)    tmpr[1][i]=lc[tmpr[1][i]];
 56         }
 57         else
 58         {
 59             k-=now;l=mid+1;
 60             for(i=1;i<=tmpr[0][0];i++)    tmpr[0][i]=rc[tmpr[0][i]];
 61             for(i=1;i<=tmpr[1][0];i++)    tmpr[1][i]=rc[tmpr[1][i]];
 62         }
 63     }
 64     return l;
 65 }
 66 int main()
 67 {
 68     int i,l,r;
 69     scanf("%d%d",&n,&m);
 70     for(i=1;i<=n;i++)
 71     {
 72         scanf("%d",&a[i]);
 73         t[++t[0]]=a[i];
 74     }
 75     for(i=1;i<=m;i++)
 76     {
 77         scanf("%s",tmp);
 78         if(tmp[0]==Q)
 79         {
 80             scanf("%d%d%d",&q[i].i,&q[i].j,&q[i].x);
 81             //q[i].t=0;
 82         }
 83         else if(tmp[0]==C)
 84         {
 85             scanf("%d%d",&q[i].i,&q[i].x);
 86             q[i].t=1;
 87             t[++t[0]]=q[i].x;
 88         }
 89     }
 90     sort(t+1,t+t[0]+1);
 91     //for(i=1;i<=t[0];i++)    printf("%d ",t[i]);puts("");
 92     totn=unique(t+1,t+t[0]+1)-t-1;
 93     for(i=1;i<=totn;i++)    ma[t[i]]=i,ma2[i]=t[i];
 94     for(i=1;i<=n;i++)    a[i]=ma[a[i]];
 95     for(i=1;i<=m;i++)
 96     {
 97         if(q[i].t==1)
 98         {
 99             q[i].x=ma[q[i].x];
100             //sz[q[i].x]++;
101         }
102     }
103     build(1,totn,root[0]);
104     //for(i=1;i<=n;i++)    root[i]=root[0];//dat[i]=dat[0];
105     for(i=1;i<=n;i++)    L=a[i],x=1,add(i);
106     for(i=1;i<=m;i++)
107     {
108         //printf("%d\n",i);
109         if(q[i].t==0)
110         {
111             printf("%d\n",ma2[query(q[i].i,q[i].j,q[i].x)]);
112         }
113         else if(q[i].t==1)
114         {
115             L=query(q[i].i,q[i].i,1);x=-1;add(q[i].i);
116             L=q[i].x;x=1;add(q[i].i);
117         }
118     }
119     return 0;
120 }

修改:先用区间第k小找出这一个点组成的"区间"的"第1小",然后将这个值的数量减去1。然后再进行加的操作。

(要卡常的话,似乎也可以另开一个数组直接进行修改,然后查找"第1小"用在那个数组中查询代替)

以上代码中大量使用了引用的技巧,还是在别人的代码里看到的,很方便。另外好像(没试过)也可以写成使得函数返回当前节点修改完成后的副本。

如果是洛谷的那道题,这样已经够了。但是zoj那道的空间卡的更紧,而且原数列元素多,操作少,因此可以用一些其他技巧:一开始建一棵静态(前缀和)主席树,另建一棵空的动态主席树。修改就在动态主席树上修改,查询则结合两部分。

不过zoj的空间貌似有点奇怪...算出来lc、rc等应该是要开256万左右的,但是貌似会MLE...改成200万就过了

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<map>
  4 #define lowbit(x) ((x)&(-x))
  5 #define mid ((l+r)>>1)
  6 using namespace std;
  7 map<int,int> ma;
  8 int ma2[60010];
  9 struct Q
 10 {
 11     int t,i,j,x;
 12 }q[10010];
 13 int n,m,totn,a[50010],t[60010];
 14 char tmp[10];
 15 int mem,root[60100],root1[50100];
 16 int L,x;
 17 int lc[2000100],rc[2000100],dat[2000100];
 18 void build(int l,int r,int& num)
 19 {
 20     num=mem++;
 21     if(l==r){dat[num]=0;return;}
 22     build(l,mid,lc[num]);
 23     build(mid+1,r,rc[num]);
 24     dat[num]=0;
 25 }
 26 void addx(int l,int r,int& num)
 27 {
 28     int t=num;num=mem++;
 29     lc[num]=lc[t];rc[num]=rc[t];
 30     if(l==r)
 31     {
 32         dat[num]=dat[t]+x;
 33         return;
 34     }
 35     if(L<=mid)    addx(l,mid,lc[num]);
 36     else    addx(mid+1,r,rc[num]);
 37     dat[num]=dat[lc[num]]+dat[rc[num]];
 38 }
 39 void add(int x){while(x<=n){addx(1,totn,root[x]);x+=lowbit(x);}}
 40 int tmpr[2][20],tmpx[2];
 41 int query(int L,int R,int k)//返回[L,R]内第k小的数
 42 {
 43     int tx,l=1,r=totn,now,i;
 44     for(tx=R,tmpr[0][0]=0;tx>0;tx-=lowbit(tx))    tmpr[0][++tmpr[0][0]]=root[tx];
 45     for(tx=L-1,tmpr[1][0]=0;tx>0;tx-=lowbit(tx))    tmpr[1][++tmpr[1][0]]=root[tx];
 46     tmpx[0]=root1[R];tmpx[1]=root1[L-1];
 47     while(l!=r)
 48     {
 49         now=0;
 50         for(i=1;i<=tmpr[0][0];i++)    now+=dat[lc[tmpr[0][i]]];
 51         for(i=1;i<=tmpr[1][0];i++)    now-=dat[lc[tmpr[1][i]]];
 52         now+=dat[lc[tmpx[0]]];now-=dat[lc[tmpx[1]]];
 53         if(now>=k)
 54         {
 55             r=mid;
 56             for(i=1;i<=tmpr[0][0];i++)    tmpr[0][i]=lc[tmpr[0][i]];
 57             for(i=1;i<=tmpr[1][0];i++)    tmpr[1][i]=lc[tmpr[1][i]];
 58             tmpx[0]=lc[tmpx[0]];tmpx[1]=lc[tmpx[1]];
 59         }
 60         else
 61         {
 62             k-=now;l=mid+1;
 63             for(i=1;i<=tmpr[0][0];i++)    tmpr[0][i]=rc[tmpr[0][i]];
 64             for(i=1;i<=tmpr[1][0];i++)    tmpr[1][i]=rc[tmpr[1][i]];
 65             tmpx[0]=rc[tmpx[0]];tmpx[1]=rc[tmpx[1]];
 66         }
 67     }
 68     return l;
 69 }
 70 int main()
 71 {
 72     int i,T;
 73     scanf("%d",&T);
 74     while(T--)
 75     {
 76         mem=t[0]=0;ma.clear();
 77         scanf("%d%d",&n,&m);
 78         for(i=1;i<=n;i++)
 79         {
 80             scanf("%d",&a[i]);
 81             t[++t[0]]=a[i];
 82         }
 83         for(i=1;i<=m;i++)
 84         {
 85             scanf("%s",tmp);
 86             if(tmp[0]==Q)
 87             {
 88                 scanf("%d%d%d",&q[i].i,&q[i].j,&q[i].x);
 89                 q[i].t=0;
 90             }
 91             else if(tmp[0]==C)
 92             {
 93                 scanf("%d%d",&q[i].i,&q[i].x);
 94                 q[i].t=1;
 95                 t[++t[0]]=q[i].x;
 96             }
 97         }
 98         sort(t+1,t+t[0]+1);
 99         //for(i=1;i<=t[0];i++)    printf("%d ",t[i]);puts("");
100         totn=unique(t+1,t+t[0]+1)-t-1;
101         for(i=1;i<=totn;i++)    ma[t[i]]=i,ma2[i]=t[i];
102         for(i=1;i<=n;i++)    a[i]=ma[a[i]];
103         for(i=1;i<=m;i++)
104         {
105             if(q[i].t==1)
106             {
107                 q[i].x=ma[q[i].x];
108                 //sz[q[i].x]++;
109             }
110         }
111         build(1,totn,root[0]);root1[0]=root[0];
112         for(i=1;i<=n;i++)    root1[i]=root1[i-1],L=a[i],x=1,addx(1,totn,root1[i]);
113         for(i=1;i<=n;i++)    root[i]=root[0];//dat[i]=dat[0];
114         //for(i=1;i<=n;i++)    L=a[i],x=1,add(i);
115         for(i=1;i<=m;i++)
116         {
117             if(q[i].t==0)
118                 printf("%d\n",ma2[query(q[i].i,q[i].j,q[i].x)]);
119             else if(q[i].t==1)
120             {
121                 L=query(q[i].i,q[i].i,1);x=-1;add(q[i].i);
122                 L=q[i].x;x=1;add(q[i].i);
123             }
124         }
125     }
126     return 0;
127 }

以上是关于洛谷 P2617 Dynamic Rankings的主要内容,如果未能解决你的问题,请参考以下文章

bzoj P2617 Dynamic Rankings

P2617 Dynamic Rankings

P2617 Dynamic Rankings

P2617 Dynamic Rankings(带修主席树)

P2617 Dynamic Rankings(整体二分)

P2617 Dynamic Rankings(动态区间第k小)