您的位置:首页 > 其它

bzoj3110 K大数查询 整体二分&树状数组

2016-03-10 19:35 375 查看
本来想用树状数组套树状数组去做的。然后发现树状数组还要动态开点。。就放弃了。然后扒了扒题解发现有整体二分啊。就自己脑补了一下。

首先,由于数有负的,但是绝对值<=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
; ll c[2]
; bool bo
; struct node{ int k,x,y,z,id; }a
,b
;
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: