【BZOJ1901】Dynamic Rankings
Description
给定一个含有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。
Input
对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。
Output
输出文件应包含T行。对于每一组数据,如果该关卡有解,输出一行Yes;否则输出一行No。
Sample Input
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
Sample Output
3
6
6
HINT
20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。
【分析】
裸的主席树,直接上模板就行了。
1 /************************************************************** 2 Problem: 1901 3 User: TCtower 4 Language: C++ 5 Result: Accepted 6 Time:612 ms 7 Memory:24988 kb 8 ****************************************************************/ 9 10 #include <iostream> 11 #include <cstring> 12 #include <cstdio> 13 #include <cmath> 14 #include <cstring> 15 #include <algorithm> 16 #include <vector> 17 //#define LOCAL 18 const int maxn=10000+5; 19 const int INF=99999999; 20 const int maxnode=2000000+10; 21 using namespace std; 22 struct OP 23 { 24 int type;//0代表询问,1代表改变 25 int l,r,k; 26 }op[maxn]; 27 //点结构体 28 struct node 29 { 30 int ls,rs,w; 31 node(){ls=rs=w=0;} 32 }T[maxnode]; 33 vector<int>LX; 34 vector<int>Q1,Q2; 35 int a[maxn],n,q,n1; 36 int cnt,root[maxn*2]; 37 38 void init(); 39 void work(); 40 //树状数组用 41 inline int lowbit(int i){return i&-i;} 42 inline int find(int i) 43 { 44 //二分查找不解释 45 return (lower_bound(LX.begin(),LX.begin()+n1,i)-LX.begin())+1; 46 } 47 void build(int &i,int l,int r,int val); 48 void query(int l,int r,int k);//区间第k大 49 //其实我觉得不传副本速度会上升? 50 int Qy(vector<int>Q1,vector<int>Q2,int l,int r,int k); 51 void my_ins(int pos,int x,int v); 52 void ins(int &i,int l,int r,int x,int v); 53 54 int main() 55 { 56 #ifdef LOCAL 57 freopen("data.txt","r",stdin); 58 freopen("out.txt","w",stdout); 59 #endif 60 init();//读入与初始化 61 work(); 62 return 0; 63 } 64 void init() 65 { 66 scanf("%d%d",&n,&q); 67 LX.clear(); 68 for (int i=1;i<=n;i++) 69 { 70 scanf("%d",&a[i]); 71 LX.push_back(a[i]); 72 } 73 char str[10]; 74 for (int i=1;i<=q;i++) 75 { 76 scanf("%s",str); 77 if (str[0]=='Q') 78 { 79 op[i].type=0; 80 scanf("%d%d%d",&op[i].l,&op[i].r,&op[i].k); 81 } 82 else 83 { 84 op[i].type=1; 85 scanf("%d%d",&op[i].l,&op[i].r); 86 LX.push_back(op[i].r); 87 } 88 } 89 sort(LX.begin(),LX.end()); 90 n1=unique(LX.begin(),LX.end())-LX.begin(); 91 } 92 void ins(int &i,int l,int r,int x,int v) 93 { 94 if (i==0) {T[++cnt]=T[i];i=cnt;}//没有创建过新的节点 95 T[i].w+=v; 96 if (l==r) return; 97 int mid=(l+r)>>1; 98 if (x<=mid) ins(T[i].ls,l,mid,x,v); 99 else ins(T[i].rs,mid+1,r,x,v); 100 } 101 void my_ins(int pos,int x,int v) 102 { 103 int t=find(x);//找到x的位置 104 for (int i=pos;i<=n;i+=lowbit(i)) 105 { 106 ins(root[i],1,n1,t,v); 107 } 108 } 109 int Qy(vector<int>Q1,vector<int>Q2,int l,int r,int k) 110 { 111 if (l==r) return l; 112 int c=0,mid=(l+r)>>1; 113 //这两句可以互换,统计总数 114 for (int i=0;i<Q1.size();i++) c-=T[T[Q1[i]].ls].w; 115 for (int i=0;i<Q2.size();i++) c+=T[T[Q2[i]].ls].w; 116 //大于k说明在左子树中而不右子树 117 for (int i=0;i<Q1.size();i++) Q1[i]=(c>=k?T[Q1[i]].ls:T[Q1[i]].rs); 118 for (int i=0;i<Q2.size();i++) Q2[i]=(c>=k?T[Q2[i]].ls:T[Q2[i]].rs); 119 if (c>=k) return Qy(Q1,Q2,l,mid,k);//继续向下寻找 120 else return Qy(Q1,Q2,mid+1,r,k-c); 121 } 122 void query(int l,int r,int k) 123 { 124 Q1.clear();Q2.clear();//临时队列清空 125 Q1.push_back(root[l!=1?l-1+n:0]); 126 Q2.push_back(root[r+n]); 127 for (int i=l-1;i;i-=lowbit(i)) Q1.push_back(root[i]); 128 for (int i=r;i;i-=lowbit(i)) Q2.push_back(root[i]); 129 int t=Qy(Q1,Q2,1,n1,k); 130 printf("%d\n",LX[t-1]); 131 } 132 void work() 133 { 134 cnt=0; 135 memset(root,0,sizeof(root)); 136 for (int i=1;i<=n;i++) 137 { 138 root[i+n]=root[i+n-1]; 139 int t=find(a[i]); 140 build(root[i+n],1,n1,t); 141 } 142 for (int i=1;i<=q;i++) 143 { 144 if (op[i].type==0) 145 query(op[i].l,op[i].r,op[i].k); 146 else 147 { 148 my_ins(op[i].l,a[op[i].l],-1); 149 my_ins(op[i].l,op[i].r,1); 150 //修改 151 a[op[i].l]=op[i].r; 152 } 153 } 154 } 155 void build(int &i,int l,int r,int val) 156 { 157 //对于修改过的每一个点 158 //都要新建一个副本 159 T[++cnt]=T[i];i=cnt; 160 T[i].w++; 161 if (l==r) return; 162 int mid=(l+r)>>1; 163 //按值建线段树 164 if (val<=mid) build(T[i].ls,l,mid,val); 165 else build(T[i].rs,mid+1,r,val); 166 }