【树套树】 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;
}
View Code

 

posted @ 2015-09-01 10:17  mithrilhan  阅读(133)  评论(0编辑  收藏  举报