【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\n",query(k)); 146 } 147 } 148 return 0; 149 }
2016-11-08 14:33:10
话说树套树的离线题可以用CDQ分治??
不会。。