• 洛谷 P2617 Dynamic Rankings || ZOJ


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

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

    建树方法:记录前缀和的各位置的线段树的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
    ",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
    ",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
    ",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
    ",i);
    109         if(q[i].t==0)
    110         {
    111             printf("%d
    ",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
    ",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 }

    upd:动态区间第k大(单点修改)不需要主席树,不需要可持久化,只需要普通的树状数组套线段树,内层动态开点即可

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define lowbit(x) ((x)&(-x))
     4 #define mid ((l+r)>>1)
     5 using namespace std;
     6 const int totn=1e9;
     7 struct Q
     8 {
     9     int t,i,j,x;
    10 }q[10010];
    11 int n,m,a[10010];
    12 char tmp[10];
    13 int mem,root[20100];
    14 int L,x;
    15 int lc[5000100],rc[5000100],dat[5000100];
    16 void addx(int l,int r,int& num)
    17 {
    18     if(!num)    num=++mem;
    19     if(l==r)
    20     {
    21         dat[num]+=x;
    22         return;
    23     }
    24     if(L<=mid)    addx(l,mid,lc[num]);
    25     else    addx(mid+1,r,rc[num]);
    26     dat[num]=dat[lc[num]]+dat[rc[num]];
    27 }
    28 void add(int x){while(x<=n){addx(1,totn,root[x]);x+=lowbit(x);}}
    29 int tmpr[2][20];
    30 int query(int L,int R,int k)//返回[L,R]内第k小的数
    31 {
    32     int tx,l=1,r=totn,now,i;
    33     for(tx=R,tmpr[0][0]=0;tx>0;tx-=lowbit(tx))    tmpr[0][++tmpr[0][0]]=root[tx];
    34     for(tx=L-1,tmpr[1][0]=0;tx>0;tx-=lowbit(tx))    tmpr[1][++tmpr[1][0]]=root[tx];
    35     while(l!=r)
    36     {
    37         now=0;
    38         for(i=1;i<=tmpr[0][0];i++)    now+=dat[lc[tmpr[0][i]]];
    39         for(i=1;i<=tmpr[1][0];i++)    now-=dat[lc[tmpr[1][i]]];
    40         if(now>=k)
    41         {
    42             r=mid;
    43             for(i=1;i<=tmpr[0][0];i++)    tmpr[0][i]=lc[tmpr[0][i]];
    44             for(i=1;i<=tmpr[1][0];i++)    tmpr[1][i]=lc[tmpr[1][i]];
    45         }
    46         else
    47         {
    48             k-=now;l=mid+1;
    49             for(i=1;i<=tmpr[0][0];i++)    tmpr[0][i]=rc[tmpr[0][i]];
    50             for(i=1;i<=tmpr[1][0];i++)    tmpr[1][i]=rc[tmpr[1][i]];
    51         }
    52     }
    53     return l;
    54 }
    55 int main()
    56 {
    57     int i,l,r;
    58     scanf("%d%d",&n,&m);
    59     for(i=1;i<=n;i++)
    60     {
    61         scanf("%d",&a[i]);
    62     }
    63     for(i=1;i<=m;i++)
    64     {
    65         scanf("%s",tmp);
    66         if(tmp[0]=='Q')
    67         {
    68             scanf("%d%d%d",&q[i].i,&q[i].j,&q[i].x);
    69             //q[i].t=0;
    70         }
    71         else if(tmp[0]=='C')
    72         {
    73             scanf("%d%d",&q[i].i,&q[i].x);
    74             q[i].t=1;
    75         }
    76     }
    77     for(i=1;i<=n;i++)    L=a[i],x=1,add(i);
    78     for(i=1;i<=m;i++)
    79     {
    80         //printf("%d
    ",i);
    81         if(q[i].t==0)
    82         {
    83             printf("%d
    ",query(q[i].i,q[i].j,q[i].x));
    84         }
    85         else if(q[i].t==1)
    86         {
    87             L=query(q[i].i,q[i].i,1);x=-1;add(q[i].i);
    88             L=q[i].x;x=1;add(q[i].i);
    89         }
    90     }
    91     return 0;
    92 }

    upd:整体二分做此题(洛谷交的):

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cstring>
      4 using namespace std;
      5 struct Q
      6 {
      7     int type,dat;
      8     int pos,fl;
      9     int l,r,num;
     10 }q[100100],qt1[100100],qt2[100100];
     11 int len,n,m,T;
     12 int qnum,ans[101000];
     13 int x[50100];
     14 #define lowbit(x) ((x)&(-x))
     15 struct BIT
     16 {
     17     int dat[50100];
     18     void addx(int pos,int x){for(;pos<=n;pos+=lowbit(pos))dat[pos]+=x;}
     19     int query(int pos){int ans=0;for(;pos>0;pos-=lowbit(pos))ans+=dat[pos];return ans;}
     20 }tt;
     21 //tt的第i位记录这一位是否小于等于mid
     22 void work(int lp,int rp,int l,int r)
     23 //在work之前,保证q[lp..rp]中询问都仅计算了所有小于l的修改操作的贡献,q[lp..rp]中不存在小于l的修改操作
     24 {
     25     if(lp>rp)    return;//不可少
     26     int i;
     27     if(l==r)
     28     {
     29         for(i=lp;i<=rp;i++)
     30             if(q[i].type==2)
     31                 ans[q[i].num]=l;
     32         return;
     33     }
     34     //tlen1=tlen2=0;
     35     int tlen1=0,tlen2=0;
     36     int mid=l+((r-l)>>1),t;
     37     for(i=lp;i<=rp;i++)
     38     {
     39         if(q[i].type==1)
     40         {
     41             if(q[i].dat<=mid)
     42             {
     43                 tt.addx(q[i].pos,q[i].fl);
     44                 qt1[++tlen1]=q[i];
     45             }
     46             else
     47                 qt2[++tlen2]=q[i];
     48         }
     49         else if(q[i].type==2)
     50         {
     51             t=tt.query(q[i].r)-tt.query(q[i].l-1);
     52             /*if(q[i].dat>=t)
     53                 qt2[++tlen2]=q[i],qt2[tlen2].dat-=t;
     54             else
     55                 qt1[++tlen1]=q[i];*/
     56             if(q[i].dat<=t)
     57                 qt1[++tlen1]=q[i];
     58             else
     59                 qt2[++tlen2]=q[i],qt2[tlen2].dat-=t;
     60         }
     61     }
     62     for(i=lp;i<=rp;i++)
     63     {
     64         if(q[i].type==1)
     65         {
     66             if(q[i].dat<=mid)
     67                 tt.addx(q[i].pos,-q[i].fl);
     68         }
     69     }
     70     //不可能每一次都暴力重置整个树状数组,又必须重置,只能把操作反着做一遍
     71     memcpy(q+lp,qt1+1,sizeof(Q)*tlen1);
     72     memcpy(q+lp+tlen1,qt2+1,sizeof(Q)*tlen2);
     73     work(lp,lp+tlen1-1,l,mid);
     74     work(lp+tlen1,rp,mid+1,r);//如果不用临时变量作为tlen,执行到上一行后tlen就会改变,导致错误
     75 }
     76 int main()
     77 {
     78     int i,a,b,c;char tmp[10];
     79     scanf("%d%d",&n,&m);
     80     len=qnum=0;
     81     for(i=1;i<=n;i++)
     82     {
     83         scanf("%d",&x[i]);
     84         q[++len].type=1;q[len].pos=i;q[len].dat=x[i];q[len].fl=1;
     85     }
     86     for(i=1;i<=m;i++)
     87     {
     88         scanf("%s",tmp);
     89         if(tmp[0]=='Q')
     90         {
     91             scanf("%d%d%d",&a,&b,&c);
     92             q[++len].type=2;q[len].l=a;q[len].r=b;q[len].dat=c;q[len].num=++qnum;
     93         }
     94         else if(tmp[0]=='C')
     95         {
     96             scanf("%d%d",&a,&b);
     97             q[++len].type=1;q[len].pos=a;q[len].dat=x[a];q[len].fl=-1;
     98             x[a]=b;
     99             q[++len].type=1;q[len].pos=a;q[len].dat=x[a];q[len].fl=1;
    100         }
    101     }
    102     work(1,len,0,1000000000);
    103     for(i=1;i<=qnum;i++)    printf("%d
    ",ans[i]);
    104     return 0;
    105 }

    以下仅做笔记

    首先事实上带修改区间k小是不满足整体二分的要求的,要做一个小小的变换:将所有信息变为以下三种操作中的一种:给某个位置加上给定值的贡献,给某个位置删去给定值的贡献,查询某个区间的k小。(不知道为什么网上有叫做“插入”和”删除“的,然而整体二分并不能解决带插入区间第k小。。。。说不定有奇怪的方法可以?再说)

    想象对于一个待处理的操作队列,以及现有的答案值域区间([l,r]),首先得到[l,r]的mid=l+(r-l)/2,然后对于所有查询操作,计算对其有贡献的(即发生在其之前的)所有值属于[l,mid]的修改操作(当然根据递归过程能保证不会有操作的值小于l的修改操作)对其的贡献(具体的话,由于这个顺序的要求,需要用一个树状数组来维护数组d[i](表示i位置的当前值带来的贡献,每一次”加上贡献“使其值加1,”删除贡献“使其值减1)的前缀和,这样在处理查询操作的时候就可以快速查询区间内有多少的贡献;由于不能暴力清零树状数组,只能把操作反着做一遍来清零)。

    然后是分治过程:

    顺次处理所有操作,并(不改变相对顺序地)将其分入两个队列。

    对于某个查询操作,操作过程非常类似线段树上二分:如果当前答案(当前小于等于mid产生的贡献)小于需要的答案,就说明取mid作为答案还不够,那么将需要的答案减去当前答案,然后归入第二个队列;如果当前答案大于等于需要的答案,就说明取mid作为答案已经够/超出了,那么直接将询问归入第一个队列。(不需要考虑对第k小更加形式化的定义。。。原来想多了)

    对于某个修改操作,如果其操作值小于等于mid,那么对第二个队列中查询操作的贡献都已经计算(在上面那个”减去当前答案“的过程中),因此只归入第一个队列;反之,说明其对第一个队列中查询操作不可能产生贡献,因此归入第二个队列。

    然后,递归处理第一个队列以及其对应值域区间[l,mid],还有第二个队列及其对应值域区间[mid+1,r]。

    当然,在函数中如果发现某一刻操作队列是空的,应该直接退出,不处理;如果发现l==r,说明当前操作队列中询问操作都已经找到正确答案,直接更新答案然后退出即可。

    复杂度?任意一个操作只会log(值域)次出现在work函数中,设f(n)为一个函数(。。。。。),如果work函数中对于长度为n的操作序列的额外消耗时间(即除了递归处理以外的时间)是O(n*f(n))的,也就是对于任意一个操作的额外消耗时间是O(f(n))的(此题中f(n)=logn),因此总复杂度是O((n+q)f(n)log(值域))(n是序列长度,q是操作个数)

  • 相关阅读:
    Spring线程池由浅入深的3个示例
    ThreadPoolExecutor之一:使用基本介绍
    Spring中的线程池ThreadPoolTaskExecutor介绍
    ThreadPoolTaskExecutor异常收集
    SPRING中的线程池ThreadPoolTaskExecutor
    ThreadPoolTaskExecutor的配置解释
    jenkins邮件配置
    使用Jenkins配置自动化构建
    Hudson和Jenkins的关系
    PV 和 UV IP
  • 原文地址:https://www.cnblogs.com/hehe54321/p/8514142.html
Copyright © 2020-2023  润新知