BZOJ 3110 【ZJOI2013】 K大数查询
Description
有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
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
1
2
1
2
1
HINT
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint
刚学了整体二分,跟随神犇的步伐走向了这道题......
神犇:这道题不是整体二分裸题吗? 我:......
也许是我真的太弱了吧:
不过好歹是A了,讲一讲我的思路:
首先,我们二分出一个答案$mid$,然后扫一遍当前区间内的询问,如果加入的数$x>=mid$,那么把这段区间的值都加$1$;这样就可以求出区间$>=mid$的数的个数了。
如果你还不会可以支持区间修改、区间查询的树状数组,请左转树状数组区间修改加区间查询。
然后,根据这些东西判断一下当前询问该丢到左边还是右边,递归处理就可以了。还有不要忘了询问的是区间第$k$大,所以对于丢到左边的询问要先把贡献给算进去。
这么做原理是什么呢?我觉得就是与普通的二分答案一样,只不过普通的二分答案只有一个询问,这里是把多个询问一起处理罢了。
下面贴代码:
1 #include<cstdio> 2 #define maxn 50010 3 4 using namespace std; 5 typedef long long llg; 6 7 struct data{ 8 int tp,l,r,k,id; 9 }s[maxn],zl[maxn],zr[maxn]; 10 int n,m,ans[maxn],tt; 11 llg c1[maxn],c2[maxn]; 12 13 int getint(){ 14 int w=0,q=0; 15 char c=getchar(); 16 while((c<'0'||c>'9')&&c!='-') c=getchar(); 17 if(c=='-') q=1,c=getchar(); 18 while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); 19 return q?-w:w; 20 } 21 22 void add(int x,int y){for(int i=x;i<=n;i+=i&(-i)) c1[i]+=y,c2[i]+=(llg)x*y;} 23 llg sum(int x){ 24 llg ans(0); 25 for(int i=x;i;i-=i&(-i)) ans+=(x+1)*c1[i]-c2[i]; 26 return ans; 27 } 28 29 void solve(int top,int end,int l,int r){ 30 if(l==r){ 31 for(int i=top;i<=end;i++) 32 ans[s[i].id]=l; 33 return; 34 } 35 int mid=l+r+1>>1,lo(0),ro(0); 36 bool ll(0),rr(0);llg x; 37 for(int i=top;i<=end;i++) 38 if(s[i].tp==1) 39 if(s[i].k>=mid) add(s[i].l,1),add(s[i].r+1,-1),zr[++ro]=s[i]; 40 else zl[++lo]=s[i]; 41 else{ 42 x=sum(s[i].r)-sum(s[i].l-1); 43 if(x>=s[i].k) zr[++ro]=s[i],rr=1; 44 else s[i].k-=x,zl[++lo]=s[i],ll=1; 45 } 46 for(int i=top;i<=end;i++) 47 if(s[i].tp==1 && s[i].k>=mid) add(s[i].l,-1),add(s[i].r+1,1); 48 for(int i=1;i<=lo;i++) s[top+i-1]=zl[i]; 49 for(int i=1;i<=ro;i++) s[top+i+lo-1]=zr[i]; 50 if(ll) solve(top,top+lo-1,l,mid-1); 51 if(rr) solve(top+lo,end,mid,r); 52 } 53 54 int main(){ 55 n=getint();m=getint(); 56 for(int i=1;i<=m;i++){ 57 s[i].tp=getint(); 58 s[i].l=getint(); s[i].r=getint(); 59 s[i].k=getint(); 60 if(s[i].tp==2) s[i].id=++tt; 61 } 62 solve(1,m,1,n); 63 for(int i=1;i<=tt;i++) 64 printf("%d\n",ans[i]); 65 return 0; 66 }