【BZOJ1901】 Zju2112 Dynamic Rankings(树套树)

【题意】

  给定一个含有n个数的序列a[1],a[2],a[3]……a[n],

  程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。

  对于每一个询问指令,你必须输出正确的回答。

  第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。

  分别表示序列的长度和指令的个数。第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。

   Q i j k 或者 C i t Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。

【分析】

  带修改的区间第k大。

  如果只是询问区间的第k大的话,就是可持久化字母树。

  然后,其实我想了挺久。。。。可以看看GDXB的题解。。http://www.cnblogs.com/KonjakJuruo/p/6031832.html

  她打的是树状数组套线段树。 其实就是树状数组搞区间,然后线段树搞数值。

  因为带修改的话,是动态的。相当于不修改的时候,我们只要记录前缀和,就可以知道某段区间的和(两个前缀相减),但是修改了之后,就要用树状数组或者线段树等维护。

  这个也是这个道理,用数据结构维护区间。

  本蒟蒻打的是线段树套字母树(区间第k大,深爱字母树,耶!)

  其实树状数组套字母树应该更简单??

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include<queue>
  7 using namespace std;
  8 #define Maxn 10010
  9 #define Maxd 32
 10  
 11 int a[Maxn],nw[Maxn],al;
 12 char s[10];
 13  
 14 struct trie
 15 {
 16     int son[2],cnt;
 17 }tr[Maxn*30*20];int tot;
 18  
 19 void upd(int x)
 20 {
 21     tr[x].son[0]=tr[x].son[1]=0;
 22     tr[x].cnt=0;
 23 }
 24  
 25 void add(int now,int y,int c)
 26 {
 27     for(int i=Maxd;i>=1;i--)
 28     {
 29         int ind=y>>i-1;
 30         y=y%(1<<i-1);
 31         if(!tr[now].son[ind])
 32         {
 33             tr[now].son[ind]=++tot;
 34             upd(tot);
 35         }
 36         now=tr[now].son[ind];
 37         tr[now].cnt+=c;
 38     }
 39 }
 40  
 41 int query(int k)
 42 {
 43     int ans=0;
 44     for(int i=Maxd;i>=1;i--)
 45     {
 46         int ls=0;
 47         for(int j=1;j<=al;j++) ls+=tr[tr[nw[j]].son[0]].cnt;
 48         if(ls>=k)
 49         {
 50             for(int j=1;j<=al;j++) nw[j]=tr[nw[j]].son[0];       
 51         }
 52         else
 53         {
 54             k-=ls;
 55             ans+=(1<<i-1);
 56             for(int j=1;j<=al;j++) nw[j]=tr[nw[j]].son[1];
 57         }
 58     }
 59     return ans;
 60 }
 61  
 62 struct node
 63 {
 64     int l,r,lc,rc,rt;
 65 }t[Maxn*2];int len;
 66  
 67  
 68 int build(int l,int r)
 69 {
 70     int x=++len;
 71     t[x].l=l;t[x].r=r;t[x].rt=++tot;
 72     upd(tot);
 73     if(l!=r)
 74     {
 75         int mid=(l+r)>>1;
 76         t[x].lc=build(l,mid);
 77         t[x].rc=build(mid+1,r);
 78     }
 79     else t[x].lc=t[x].rc=0;
 80     return x;
 81 }
 82  
 83 void change(int x,int y,int z)
 84 {
 85     add(t[x].rt,z,1);
 86     if(a[y]!=-1) add(t[x].rt,a[y],-1);
 87     if(t[x].l==t[x].r) return;
 88     int mid=(t[x].l+t[x].r)>>1;
 89     if(y<=mid) change(t[x].lc,y,z);
 90     else change(t[x].rc,y,z);
 91 }
 92  
 93 void ffind(int x,int l,int r)
 94 {
 95     if(t[x].l==l&&t[x].r==r)
 96     {
 97         nw[++al]=t[x].rt;
 98         return;
 99     }
100     int mid=(t[x].l+t[x].r)>>1;
101     if(r<=mid) ffind(t[x].lc,l,r);
102     else if(l>mid) ffind(t[x].rc,l,r);
103     else
104     {
105         ffind(t[x].lc,l,mid);
106         ffind(t[x].rc,mid+1,r);
107     }
108 }
109  
110 int n,m;
111 void init()
112 {
113     len=0;tot=0;
114     scanf("%d%d",&n,&m);
115     build(1,n);
116     memset(a,-1,sizeof(a));
117     for(int i=1;i<=n;i++)
118     {
119         int x;
120         scanf("%d",&x);
121         change(1,i,x);
122         a[i]=x;
123     }
124 }
125  
126 int main()
127 {
128     init();
129     for(int i=1;i<=m;i++)
130     {
131         scanf("%s",s);
132         if(s[0]=='C')
133         {
134             int x,y;
135             scanf("%d%d",&x,&y);
136             change(1,x,y);
137             a[x]=y;
138         }
139         else
140         {
141             int x,y,k;
142             scanf("%d%d%d",&x,&y,&k);
143             al=0;
144             ffind(1,x,y);
145             printf("%d
",query(k));
146         }
147     }
148     return 0; 
149 }
View Code

2016-11-08 14:33:10

话说树套树的离线题可以用CDQ分治??

不会。。

原文地址:https://www.cnblogs.com/Konjakmoyu/p/6042943.html