BZOJ4923 [Lydsy1706月賽]K小值查詢
阿新 • • 發佈:2019-01-12
題意
維護一個長度為n的正整數序列a_1,a_2,...,a_n,支援以下兩種操作:
1 k,將序列a從小到大排序,輸出a_k的值。
2 k,將所有嚴格大於k的數a_i減去k。
\(n \leq 10^5\)
分析
考慮用Treap來維護數的排名。感官上有些數修改之後的相對排名時不會變的。
考慮1到k的數不會被修改。k+1到2k的數修改之後會和前面的數排名交叉,2k+1到inf的數修改後相對排名不變。後面的數打個標記即可。中間的數至少會減少一半,暴力修改插入即可。權值只減不增,類似啟發式合併的攤還分析一樣,一個數最多會被這樣減小\(O(\log n)\)次。
時間複雜度\(O(n \log^2 n)\)
程式碼
#include<bits/stdc++.h> #define rg register #define il inline #define co const template<class T>il T read() { rg T data=0; rg int w=1; rg char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') w=-1; ch=getchar(); } while(isdigit(ch)) { data=data*10+ch-'0'; ch=getchar(); } return data*w; } template<class T>il T read(rg T&x) { return x=read<T>(); } typedef long long ll; co int N=1e5+7; int root; int tot; namespace T { int ch[N][2],siz[N]; int val[N],sub[N]; int pri[N]; int newnode(int v) { int x=++tot; ch[x][0]=ch[x][1]=0,siz[x]=1; val[x]=v,sub[x]=0; pri[x]=rand(); return x; } void pushup(int x) { siz[x]=siz[ch[x][0]]+1+siz[ch[x][1]]; } void pushdown(int x) { if(sub[x]) { if(ch[x][0]) { sub[ch[x][0]]+=sub[x]; val[ch[x][0]]-=sub[x]; } if(ch[x][1]) { sub[ch[x][1]]+=sub[x]; val[ch[x][1]]-=sub[x]; } sub[x]=0; } } void split(int x,int v,int&l,int&r) { if(!x) { l=r=0; return; } if(val[x]<=v) { l=x; pushdown(l); split(ch[l][1],v,ch[l][1],r); pushup(l); } else { r=x; pushdown(r); split(ch[r][0],v,l,ch[r][0]); pushup(r); } } int merge(int x,int y) { if(!x||!y) return x+y; if(pri[x]>pri[y]) { pushdown(x); ch[x][1]=merge(ch[x][1],y); pushup(x); return x; } else { pushdown(y); ch[y][0]=merge(x,ch[y][0]); pushup(y); return y; } } void insert(int&t,int v) { int x,y; split(t,v,x,y); t=merge(x,merge(newnode(v),y)); } void inserts(int&t,int x) { if(!x) return; pushdown(x); inserts(t,ch[x][0]); inserts(t,ch[x][1]); ch[x][0]=ch[x][1]=0,siz[x]=1; int l,r; split(t,val[x],l,r); t=merge(l,merge(x,r)); } void change(int&t,int v) { int x,y,z; split(t,v,x,y); if(y) { sub[y]+=v; val[y]-=v; } split(y,v,y,z); inserts(x,y); t=merge(x,z); } int kth(int&t,int k) { pushdown(t); if(k==siz[ch[t][0]]+1) return val[t]; if(k<=siz[ch[t][0]]) return kth(ch[t][0],k); else return kth(ch[t][1],k-siz[ch[t][0]]-1); } } using namespace T; using namespace std; int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); srand(20030506); int n,m; read(n),read(m); while(n--) insert(root,read<int>()); while(m--) { int opt,k; read(opt),read(k); if(opt==1) printf("%d\n",kth(root,k)); else change(root,k); } return 0; }