标签:
刚学了整体二分,跟随神犇的步伐走向了这道题......
神犇:这道题不是二分答案裸题吗? 我:......
也许是我真的太弱了吧:
不过好歹是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 }
标签:
原文地址:http://www.cnblogs.com/lcf-2000/p/5547552.html