【树套树】 BZOJ 3110 K大数
题意:有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c,如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少
思路:先开一颗权值线段树。对于当前结点K,它表示了权值范围为a~b的所有结点的信息。但是有人要问:这样怎么控制位置范围是l~r这个要求呢?我们可以在这个点上再开一棵表示位置的信息。那么在第二重树中的结点sum[k]表示在a~b的权值范围内,位置范围是l~r的点的个数。查找的时候是BST原理
代码:
#include<cstdio> #include<algorithm> using namespace std; const int N=50000+5; const int M=N*16*16; int root[N*4],n,m,sum[M],left[M],right[M],lazy[M],c,L,R,cnt,opt; void put(int &k,int l,int r){ if (!k) k=++cnt; if (L<=l&&r<=R) {lazy[k]++;sum[k]+=(r-l+1);return;} int mid=(l+r)/2; if (L<=mid) put(left[k],l,mid); if (R>mid) put(right[k],mid+1,r); sum[k]=sum[left[k]]+sum[right[k]]+lazy[k]*(r-l+1); } void update(int now,int l,int r){ put(root[now],1,n); if (l==r) return;int mid=(l+r)/2; if (c<=mid) update(now*2,l,mid); else update(now*2+1,mid+1,r); } int calc(int k,int l,int r){ if (!k) return 0; if (L<=l&&r<=R) return sum[k]; int mid=(l+r)/2,temp=0; if (L<=mid) temp+=calc(left[k],l,mid); if (R>mid) temp+=calc(right[k],c m id+1,r); return temp+lazy[k]*(min(R,r)-max(L,l)+1); } int ask(int now,int l,int r) { if (l==r) return l; int mid=(l+r)/2,temp=calc(root[now*2],1,n); if (c<=temp) return ask(now*2,l,mid); c-=temp;return ask(now*2+1,mid+1,r); } int main() { scanf("%d%d", &n, &m); while (m-- > 0) { scanf("%d%d%d%d", &opt, &L, &R, &c); if (opt==1) c=n-c+1,update(1,1,n); else printf("%d\n",n-ask(1,1,n)+1); } return 0; }