本来想用树状数组套树状数组去做的。然后发现树状数组还要动态开点。。就放弃了。然后扒了扒题解发现有整体二分啊。就自己脑补了一下。
首先,由于数有负的,但是绝对值<=N,于是我们可以用N减去这个数再+1,就变成了(1...2N+1)范围内的数了,然后求第k大的就变成第k小的了,最后只需要输出的时候再减回来即可。
然后用一个状态(l,r,{S}),表示查询的答案在(l,r)中,或者修改的数的大小在(l,r)中的操作的集合为{S}。那么令mid=(l+r)/2,然后就可以通过只执行修改的数在(l,mid)中的修改操作,来判断询问的答案是否在(l,mid)中了。于是可以递归调用(l,mid,{S1})(mid+1,r,{S2})。最后当l=r时显然此时(l,r,{S})中所有询问的答案为l。
说的好乱不懂看代码。。。。
AC代码如下:
#include<iostream> #include<cstdio> #define ll long long #define N 100005 using namespace std; int n,m,tot,ans[N]; ll c[2][N]; bool bo[N]; struct node{ int k,x,y,z,id; }a[N],b[N]; void ins(int k,int x,int t){ for (; x<=tot; x+=x&-x) c[k][x]+=t; } ll getsum(int k,int x){ ll t=0; for (; x; x-=x&-x) t+=c[k][x]; return t; } void mdy(int x,int y,int z){ ins(0,x,z); ins(1,x,z*(x-1)); ins(0,y+1,-z); ins(1,y+1,-z*y); } ll qry(int x,int y){ return getsum(0,y)*y-getsum(1,y)-getsum(0,x-1)*(x-1)+getsum(1,x-1); } int read(){ int x=0,fu=1; char ch=getchar(); while (ch<'0' || ch>'9'){ if (ch=='-') fu=-1; ch=getchar(); } while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x*fu; } void solve(int x,int y,int l,int r){ int i,j=x,k=x,mid=(l+r)>>1; if (l==r){ for (i=x; i<=y; i++) if (a[i].k) ans[a[i].id]=l; return; } for (i=x; i<=y; i++) if (a[i].k){ ll tmp=qry(a[i].x,a[i].y); if (tmp<a[i].z){ bo[i]=0; a[i].z-=(int)tmp; } else{ bo[i]=1; k++; } }else if (a[i].z<=mid){ mdy(a[i].x,a[i].y,1); bo[i]=1; k++; } else bo[i]=0; for (i=x; i<=y; i++) if (!a[i].k && a[i].z<=mid) mdy(a[i].x,a[i].y,-1); for (i=x; i<=y; i++) if (bo[i]) b[j++]=a[i]; else b[k++]=a[i]; for (i=x; i<=y; i++) a[i]=b[i]; solve(x,j-1,l,mid); solve(j,y,mid+1,r); } int main(){ n=read(); m=read(); int i,cnt=0; for (i=1; i<=m; i++){ a[i].k=read()-1; a[i].x=read(); a[i].y=read(); a[i].z=read(); if (a[i].k) a[i].id=++cnt; else{ a[i].z=n-a[i].z+1; tot=max(tot,a[i].z); } } solve(1,m,1,tot); for (i=1; i<=cnt; i++) printf("%d\n",n-ans[i]+1); return 0; }
by lych
2016.3.10