BZOJ3110[Zjoi2013]K大数查询(树状数组+整体二分)
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
Input
第一行N,M
接下来M行,每行形如1 a b c或2 a b c
Output
输出每个询问的结果
Sample Input
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
2
1
HINT
【样例说明】
第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1
的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是
1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3
大的数是 1 。
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint
题解:
题意概括
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c。如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint
题解
让我们来考虑神奇的分治算法。
整体二分!!(当你会了)
首先当你已经掌握了树状数组的区间加和区间询问(如果不会->点这里)
我们考虑二分答案。
注意进行以下操作要严格按照输入时间先后顺序来。
首先对于加进去的数字c,我们把他变成n-c+1,这样就把询问前k大变成了前k小。
如果是修改操作,如果修改的值比当前的mid值小,就修改,并扔到左区间里面。否则扔到右边。
如果是询问操作,如果在当前的状态下,该询问的区间内查询到的数的个数res比当前询问的c要大(或者相等),那么显然答案在左区间,把他扔到左边,否则把他的c减掉res再扔到右边去。
然后递归分治两个区间就可以了。
(本质是个二分答案的升级版)
参考代码:
1 /************************************************************** 2 Problem: 3110 3 User: SongHL 4 Language: C++ 5 Result: Accepted 6 Time:1880 ms 7 Memory:3640 kb 8 ****************************************************************/ 9 10 #include<bits/stdc++.h> 11 using namespace std; 12 #define lowbit(x) x&-x 13 #define clr(a,b) memset(a,b,sizeof a) 14 typedef long long ll; 15 const int maxn=50005; 16 int n,m,id[maxn],templ[maxn],tempr[maxn]; 17 ll tree[2][maxn]; 18 inline int read() 19 { 20 int x=0,f=1;char ch=getchar(); 21 while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();} 22 while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();} 23 return x*f; 24 } 25 inline void add(int t,int x,int y)//单点加 26 { 27 while(x<=n+1) 28 { 29 tree[t][x]+=y; 30 x+=lowbit(x); 31 } 32 } 33 inline void update(int l,int r,int val)//区间加 34 { 35 add(0,l,val);add(1,l,l*val); 36 add(0,r+1,-val);add(1,r+1,-val*(r+1)); 37 } 38 inline ll Sum(int t,int x)//前缀和 39 { 40 ll ans=0; 41 while(x>0) 42 { 43 ans+=tree[t][x]; 44 x-=lowbit(x); 45 } 46 return ans; 47 } 48 inline ll query(int l,int r)//区间求和 49 { 50 return Sum(0,r)*(r+1)-Sum(0,l)*l-Sum(1,r)+Sum(1,l); 51 } 52 struct Node{ 53 int type,l,r,x,ans; 54 void Get() 55 { 56 type=read();l=read();r=read();x=read(); 57 if(type==1) x=n-x+1; 58 } 59 } a[maxn]; 60 61 inline void Solve(int lx,int rx,int l,int r) 62 { 63 if(l>r) return ; 64 if(lx==rx) 65 { 66 for(int i=l;i<=r;++i) a[id[i]].ans=lx; 67 return ; 68 } 69 int midx=lx+rx>>1; 70 int L=0,R=0; 71 for(int i=l;i<=r;++i) 72 { 73 if(a[id[i]].type==1) 74 { 75 if(a[id[i]].x<=midx) templ[++L]=id[i],update(a[id[i]].l,a[id[i]].r,1); 76 else tempr[++R]=id[i]; 77 } 78 else 79 { 80 ll res=query(a[id[i]].l,a[id[i]].r); 81 if(res>=a[id[i]].x) templ[++L]=id[i]; 82 else tempr[++R]=id[i],a[id[i]].x-=res; 83 } 84 } 85 for(int i=1;i<=L;++i) 86 { 87 if(a[templ[i]].type==1) 88 update(a[templ[i]].l,a[templ[i]].r,-1); 89 } 90 91 for(int i=l;i<=l+L-1;++i) id[i]=templ[i-(l-1)]; 92 for(int i=r-R+1;i<=r;++i) id[i]=tempr[i-(r-R)]; 93 94 Solve(lx,midx,l,l+L-1); 95 Solve(midx+1,rx,r-R+1,r); 96 } 97 98 int main() 99 { 100 n=read();m=read(); 101 for(int i=1;i<=m;++i) a[i].Get(),id[i]=i; 102 clr(tree,0); 103 Solve(1,2*n+1,1,m); 104 for(int i=1;i<=m;++i) 105 if(a[i].type==2) printf("%d\n",n-a[i].ans+1); 106 return 0; 107 }