bzoj 3065
3065: 带插入区间K小值
Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 5067 Solved: 1658
[Submit][Status][Discuss]
Description
从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。
这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。
这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。
这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)
这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。
请你帮一帮伏特吧。
快捷版题意:带插入、修改的区间k小值在线查询。
Input
第一行一个正整数n,表示原来有n只跳蚤排成一行做早操。
第二行有n个用空格隔开的非负整数,从左至右代表每只跳蚤的弹跳力。
第三行一个正整数q,表示下面有多少个操作。
下面一共q行,一共三种操作对原序列的操作:(假设此时一共m只跳蚤)
1. Q x y k: 询问从左至右第x只跳蚤到从左至右第y只跳蚤中,弹跳力第k小的跳蚤的弹跳力是多少。
(1 <= x <= y <= m, 1 <= k <= y - x + 1)
2. M x val: 将从左至右第x只跳蚤的弹跳力改为val。
(1 <= x <= m)
3. I x val: 在从左至右第x只跳蚤的前面插入一只弹跳力为val的跳蚤。即插入后从左至右第x只跳蚤是我刚插入的跳蚤。
(1 <= x <= m + 1)
为了体现在线操作,设lastAns为上一次查询的时候程序输出的结果,如果之前没有查询过,则lastAns = 0。
则输入的时候实际是:
Q _x _y _k ------> 表示 Q _x^lastAns _y^lastAns _k^lastAns
M _x _val ------> 表示 M _x^lastAns _val^lastAns
I _x _val ------> 表示 I _x^lastAns _val^lastAns
简单来说就是操作中输入的整数都要异或上一次询问的结果进行解码。
(祝Pascal的同学早日转C++,就不提供pascal版的描述了。)
Output
对于每个询问输出回答,每行一个回答。
Sample Input
10 5 8 28 0 19 2 31 1 22
30
I 6 9
M 1 11
I 8 17
M 1 31
M 6 26
Q 2 7 6
I 23 30
M 31 7
I 22 27
M 26 18
Q 26 17 31
I 5 2
I 18 13
Q 3 3 3
I 27 19
Q 23 23 30
Q 5 13 5
I 3 0
M 15 27
Q 0 28 13
Q 3 29 11
M 2 8
Q 12 5 7
I 30 19
M 11 19
Q 17 8 29
M 29 4
Q 3 0 12
I 7 18
M 29 27
Sample Output
2
31
0
14
15
14
27
15
14
HINT
此题作为一个小小的研究来搞吧~做法有很多~不知道这题究竟有多少种做法。
请自觉O(log^2n),我故意卡块状链表,块链A了的请受我深情一拜……
A掉的同学请在Discuss里面简要说下自己的做法吧~
原序列长度 <= 35000
插入个数 <= 35000,修改个数 <= 70000,查询个数 <= 70000 ,0 <= 每时每刻的权值 <= 70000
由于是OJ上的题,所以数据无梯度。为了防止卡OJ,本题只有4组数据。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int const N=70000+10; 4 int const B=500; 5 #define st(x) ((x-1)*B+1) 6 #define ed(x) min(n,x*B) 7 #define id(x) ((x-1)/B+1) 8 vector<int> a[500],b[500]; 9 int n,t[N],q,sum; 10 int calc(int x,int y,int mid){ 11 int tx,ty,k1=0,k2=0; 12 for(tx=1;tx<=sum && k1<x;tx++) k1+=a[tx].size(); 13 tx--; k1-=a[tx].size(); 14 for(ty=1;ty<=sum && k2<y;ty++) k2+=a[ty].size(); 15 ty--; k2-=a[ty].size(); 16 int s=0; 17 int sx=x-k1-1; 18 int sy=y-k2-1; 19 if(tx==ty){ 20 for(int i=sx;i<=sy;i++) 21 if(a[tx][i]<=mid) s++; 22 return s; 23 } 24 for(int i=sx;i<a[tx].size();i++) 25 if(a[tx][i]<=mid) s++; 26 for(int i=0;i<=sy;i++) 27 if(a[ty][i]<=mid) s++; 28 for(int i=tx+1;i<=ty-1;i++){ 29 int k=upper_bound(b[i].begin(),b[i].end(),mid)-b[i].begin(); 30 s+=k; 31 } 32 return s; 33 } 34 int main(){ 35 scanf("%d",&n); 36 for(int i=1;i<=n;i++) 37 scanf("%d",&t[i]); 38 sum=id(n); 39 for(int i=1;i<=sum;i++){ 40 for(int j=st(i);j<=ed(i);j++){ 41 a[i].push_back(t[j]); 42 b[i].push_back(t[j]); 43 } 44 sort(b[i].begin(),b[i].end()); 45 } 46 int last=0; 47 scanf("%d",&q); 48 while (q--){ 49 char s[2]; 50 scanf("%s",s); 51 if(s[0]=='Q'){ 52 int l=0,r=N,x,y,k; 53 scanf("%d%d%d",&x,&y,&k); 54 x^=last;y^=last;k^=last; 55 while (l<r){ 56 int mid=(l+r)/2; 57 if(calc(x,y,mid)>=k)r=mid; 58 else l=mid+1; 59 } 60 printf("%d\n",last=l); 61 } 62 if(s[0]=='M'){ 63 int x,y; 64 scanf("%d%d",&x,&y); 65 x^=last;y^=last; 66 int k=0,i; 67 for(i=1;i<=sum && k<x;i++) k+=a[i].size(); 68 i--; 69 k-=a[i].size(); 70 int v=a[i][x-k-1]; 71 a[i][x-k-1]=y; 72 for(int j=0;j<b[i].size();j++) 73 if(b[i][j]==v){ 74 b[i][j]=y; 75 sort(b[i].begin(),b[i].end()); 76 break; 77 } 78 } 79 if(s[0]=='I'){ 80 int x,y; 81 scanf("%d%d",&x,&y) ; 82 x^=last;y^=last; 83 int k=0,i; n++; 84 for(i=1;i<=sum && k<x;i++) k+=a[i].size(); 85 i--; 86 k-=a[i].size(); 87 int t=x-k-1; 88 a[i].insert(a[i].begin()+t,y); 89 b[i].push_back(y); 90 sort(b[i].begin(),b[i].end()); 91 if(a[i].size()>=2*B){ 92 for(int j=sum;j>i;j--) 93 a[j+1]=a[j],b[j+1]=b[j]; 94 a[i+1].clear(); 95 b[i+1].clear(); 96 for(int j=B;j<2*B;j++) 97 a[i+1].push_back(a[i][j]),b[i+1].push_back(a[i][j]); 98 a[i].erase(a[i].begin()+B,a[i].end()); 99 b[i].clear(); 100 for(int j=0;j<B;j++) b[i].push_back(a[i][j]); 101 sort(b[i].begin(),b[i].end()); 102 sort(b[i+1].begin(),b[i+1].end()); 103 sum++; 104 } 105 } 106 } 107 return 0; 108 }
方法2:替罪羊树套权值线段树,重构的时候还写了一波线段树合并(感觉不写线段树合并也没事,这里合并的时候节点不能共用,因为要修改),空间无限大,还需要空间回收。
求答案的时候二分查找, 每个询问需要$(logn*logn)$,总的时间复杂度$O(n*logn*logn)$,但是常数很大,还不如分块来的块。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int const N=70000; 4 int const M=20000000; 5 int sz[N<<1],lc[N<<1],rc[N<<1],n,m,lch[M],rch[M],size[M],rt[N<<1],root,val[N<<1]; 6 int a[N],b[N],c[N],top,top2,st[N<<1],st2[M]; 7 void merge(int &x,int x1,int x2,int l,int r){ 8 if(!x1 && !x2) return; 9 x=st2[top2--]; 10 size[x]=size[x1]+size[x2]; 11 int mid=(l+r)/2; 12 merge(lch[x],lch[x1],lch[x2],l,mid); 13 merge(rch[x],rch[x1],rch[x2],mid+1,r); 14 } 15 void insert(int &x,int l,int r,int v,int t){ 16 if(!x) x=st2[top2--]; 17 if(l==r){ 18 size[x]+=t; 19 return; 20 } 21 int mid=(l+r)/2; 22 if(v<=mid) insert(lch[x],l,mid,v,t); 23 else insert(rch[x],mid+1,r,v,t); 24 size[x]=size[lch[x]]+size[rch[x]]; 25 } 26 void build(int &x,int l,int r){ 27 if(l>r) return; 28 if(!x) x=st[top--]; 29 int mid=(l+r)/2; 30 val[x]=a[mid]; 31 build(lc[x],l,mid-1); 32 build(rc[x],mid+1,r); 33 sz[x]=1+sz[lc[x]]+sz[rc[x]]; 34 merge(rt[x],rt[lc[x]],rt[rc[x]],0,N); 35 insert(rt[x],0,N,a[mid],1); 36 } 37 int del(int x,int k,int v){ 38 int num=sz[lc[x]]; 39 if(num+1==k){ 40 insert(rt[x],0,N,val[x],-1); 41 insert(rt[x],0,N,v,1); 42 int t=val[x]; 43 val[x]=v; 44 return t; 45 }else { 46 int t; 47 if(num>=k) t=del(lc[x],k,v); 48 else t=del(rc[x],k-num-1,v); 49 insert(rt[x],0,N,t,-1); 50 insert(rt[x],0,N,v,1); 51 return t; 52 } 53 } 54 void dfs2(int x,int l,int r){ 55 if(!x) return ; 56 st2[++top2]=x; 57 int mid=(l+r)/2; 58 dfs2(lch[x],l,mid); 59 dfs2(rch[x],mid+1,r); 60 lch[x]=rch[x]=size[x]=0; 61 } 62 void dfs(int x){ 63 if(!x) return ; 64 dfs2(rt[x],0,N); 65 dfs(lc[x]); 66 a[++a[0]]=val[x]; 67 st[++top]=x; 68 dfs(rc[x]); 69 lc[x]=rc[x]=rt[x]=sz[x]=val[x]=0; 70 } 71 void rebuild(int &x){ 72 a[0]=0; 73 dfs(x); 74 int t=0; 75 build(t,1,a[0]); 76 x=t; 77 } 78 void add(int &x,int k,int v){ 79 if(!x) { 80 x=st[top--]; 81 val[x]=v; 82 sz[x]=1; 83 insert(rt[x],0,N,v,1); 84 return; 85 } 86 int num=sz[lc[x]]; 87 insert(rt[x],0,N,v,1); 88 if(num>=k) add(lc[x],k,v); 89 else add(rc[x],k-num-1,v); 90 sz[x]=sz[lc[x]]+sz[rc[x]]+1; 91 if(max(sz[lc[x]],sz[rc[x]])>0.66*sz[x])rebuild(x); 92 } 93 void get(int x,int l,int r,int ll,int rr){ 94 if(l>r) return; 95 if(ll<=l && r<=rr) { 96 b[++b[0]]=rt[x]; 97 return; 98 } 99 int mid=l+sz[lc[x]]-1; 100 if(mid+1>=ll && mid+1<=rr) 101 { 102 c[++c[0]]=val[x]; 103 } 104 if(ll<=mid) get(lc[x],l,mid,ll,rr); 105 if(rr>=mid+2) get(rc[x],mid+2,r,ll,rr); 106 } 107 int main(){ 108 scanf("%d",&n); 109 for(int i=1;i<=n;i++){ 110 scanf("%d",&a[i]); 111 } 112 top=100000; 113 top2=M-1; 114 for(int i=1;i<=top;i++) st[i]=top-i+1; 115 for(int i=1;i<=top2;i++)st2[i]=top2-i+1; 116 build(root,1,n); 117 scanf("%d",&m); 118 int last=0; 119 while (m--){ 120 char s[2]; 121 scanf("%s",s); 122 if(s[0]=='M') { 123 int x,y; 124 scanf("%d%d",&x,&y); 125 x^=last; y^=last; 126 del(root,x,y); 127 } 128 if(s[0]=='I'){ 129 int x,y; 130 scanf("%d%d",&x,&y); 131 x^=last; y^=last; 132 add(root,x-1,y); 133 n++; 134 } 135 if(s[0]=='Q'){ 136 int x,y,k; 137 scanf("%d%d%d",&x,&y,&k); 138 x^=last;y^=last; k^=last; 139 b[0]=c[0]=0; 140 get(root,1,n,x,y); 141 int l=0,r=N; 142 while (l<r){ 143 int mid=(l+r)/2; 144 int ts=0; 145 for(int i=1;i<=c[0];i++) if(c[i]>=l && c[i]<=mid) ts++; 146 for(int i=1;i<=b[0];i++) ts+=size[lch[b[i]]]; 147 if(ts>=k) { 148 for(int i=1;i<=b[0];i++) b[i]=lch[b[i]]; 149 r=mid; 150 }else { 151 for(int i=1;i<=b[0];i++) b[i]=rch[b[i]]; 152 k-=ts; 153 l=mid+1; 154 } 155 } 156 printf("%d\n",last=l); 157 } 158 } 159 return 0; 160 }