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

Sample Output

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 }

 

posted @ 2016-05-31 21:09  lcf2000  阅读(827)  评论(0编辑  收藏  举报