BZOJ3110:[ZJOI2013]K大數查詢
阿新 • • 發佈:2018-12-24
淺談樹狀陣列與線段樹:https://www.cnblogs.com/AKMer/p/9946944.html
外層一個權值線段樹,內層一個位置線段樹。對於外層權值線段樹上的結點\(p\),它所屬的內層線段樹上記錄每個點上有多少個值在外層的\(l,r\)內。
對於加數,直接把外層線段樹權值區間包涵要加的權值的所有點所屬的內層線段樹的區間\(l,r\)加一。
對於查詢,直接在外層線段樹上二分查詢即可。
時間複雜度:\(O(nlog^2n)\)
空間複雜度:\(O(nlog^2n)\)
程式碼如下:
#include <cstdio> using namespace std; typedef long long ll; const int maxn=5e4+5,maxsz=1.28e7; int n,m; int read() { int x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0'; return x*f; } struct pos_segment_tree { int tot; ll sum[maxsz]; int ls[maxsz],rs[maxsz],tag[maxsz]; void add(int &p,int l,int r,int L,int R) { if(!p)p=++tot;sum[p]+=R-L+1; if(L<=l&&r<=R) {tag[p]++;return;} int mid=(l+r)>>1; if(R<=mid)add(ls[p],l,mid,L,R); else if(L>mid)add(rs[p],mid+1,r,L,R); else add(ls[p],l,mid,L,mid),add(rs[p],mid+1,r,mid+1,R); } ll query(int p,int l,int r,int L,int R) { if(L<=l&&r<=R)return sum[p]; int mid=(l+r)>>1;ll res=1ll*tag[p]*(R-L+1); if(R<=mid)res+=query(ls[p],l,mid,L,R); else if(L>mid)res+=query(rs[p],mid+1,r,L,R); else res+=query(ls[p],l,mid,L,mid)+query(rs[p],mid+1,r,mid+1,R); return res; } }T_inside; struct val_segment_tree { int rt[maxn<<2]; void change(int p,int l,int r,int pos,int L,int R) { while(1) { T_inside.add(rt[p],1,n,L,R); if(l==r)break;int mid=(l+r)>>1; if(pos<=mid)p<<=1,r=mid; else p=p<<1|1,l=mid+1; } } int query(int p,int l,int r,int L,int R,int rk) { while(l!=r) { int mid=(l+r)>>1; ll tmp=T_inside.query(rt[p<<1|1],1,n,L,R); if(tmp>=rk)p=p<<1|1,l=mid+1; else p=p<<1,r=mid,rk-=tmp; } return l; } }T_out; int main() { n=read(),m=read(); for(int i=1;i<=m;i++) { int opt=read(),l=read(),r=read(),k=read(); if(opt==1)T_out.change(1,1,n,k,l,r); else printf("%d\n",T_out.query(1,1,n,l,r,k)); } return 0; }