您的位置:首页 > 其它

BZOJ 3110 [Zjoi2013]K大数查询

2016-03-08 14:56 417 查看
title: ‘BZOJ 3110 [Zjoi2013]K大数查询’

categories: BZOJ

date: 2016-2-3 00:00:00

tags: [树套树,整体二分]

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.txt

2 5

1 1 2 1

1 1 2 2

2 1 1 2

2 1 1 1

2 1 2 3

output.txt

1

2

1

Solution

好久没更博客了。。

首先这题一个位置可以加多个数,一开始没有看懂题意很坑爹。。。

这样一来,我们可以对开一个二维线段树,外层是权值线段树,内层是区间线段树。这样做的意义是,对每一个权值的区间都用一个线段树来维护其出现的位置和次数。因为树套树太耗内存了,还是在修改的时候新建节点比较好。。。

在外层那颗线段树写类似于二分的非递归就好了,因为并不需要用子节点来更新他的信息。

还有因为我太弱了所以并没有写标记永久化……感觉要去学习一下新姿势了。

感觉一般可以用树套树做的题目整体二分也是可以的?权值那层线段树只需要二分就好了,然后再用一个线段树来处理整体二分时候对询问信息的处理。

Code 1:树套树

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define maxn (50000+5)
#define maxm (5000000+5)

using namespace std;

struct Seg_Tree{
int lc,rc,sum,tag;
}tr[maxm<<2];

int root[maxn<<2];
int n,m,cnt;

inline int in(){
int x=0;
char ch=getchar();
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}

void Update(int ind){
tr[ind].sum=tr[tr[ind].lc].sum+tr[tr[ind].rc].sum;
}

void Pushdown(int ind,int l,int r){
if(tr[ind].tag && l!=r){
if(!tr[ind].lc) tr[ind].lc=++cnt;
if(!tr[ind].rc) tr[ind].rc=++cnt;
int L=tr[ind].lc,R=tr[ind].rc,m=(l+r)>>1;
tr[L].tag+=tr[ind].tag; tr[R].tag+=tr[ind].tag;
tr[L].sum+=tr[ind].tag*(m-l+1); tr[R].sum+=tr[ind].tag*(r-m);
}
tr[ind].tag=0;
}

void Modify(int &ind,int l,int r,int ql,int qr){
if(!ind) ind=++cnt;
Pushdown(ind,l,r);
if(l==ql && r==qr){
tr[ind].sum+=(r-l+1); tr[ind].tag++;
return;
}
int mid=(l+r)>>1;
if(qr<=mid) Modify(tr[ind].lc,l,mid,ql,qr);
else if(ql>mid) Modify(tr[ind].rc,mid+1,r,ql,qr);
else Modify(tr[ind].lc,l,mid,ql,mid),Modify(tr[ind].rc,mid+1,r,mid+1,qr);
Update(ind);
}

int Getsum(int ind,int l,int r,int ql,int qr){
if(!ind) return 0;
Pushdown(ind,l,r);
if(l==ql && r==qr) return tr[ind].sum;
int mid=(l+r)>>1,tmp;
if(qr<=mid) tmp=Getsum(tr[ind].lc,l,mid,ql,qr);
else if(ql>mid) tmp=Getsum(tr[ind].rc,mid+1,r,ql,qr);
else tmp=Getsum(tr[ind].lc,l,mid,ql,mid)+Getsum(tr[ind].rc,mid+1,r,mid+1,qr);
Update(ind);
return tmp;
}

void Insert(int a,int b,int c){
int l=1,r=n,ind=1;
while(l<r){
Modify(root[ind],1,n,a,b);
int mid=(l+r)>>1;
if(c<=mid) r=mid,ind=ind<<1;
else l=mid+1,ind=ind<<1|1;
}
Modify(root[ind],1,n,a,b);
}

int Query(int a,int b,int c){
int l=1,r=n,ind=1;
while(l<r){
int mid=(l+r)>>1;
int s=Getsum(root[ind<<1],1,n,a,b);
if(s>=c) r=mid,ind=ind<<1;
else l=mid+1,ind=ind<<1|1,c-=s;
}
return l;
}

int main(){
n=in(); m=in();
for(int i=1;i<=m;i++){
int opt,a,b,c;
opt=in(); a=in(); b=in(); c=in();
if(opt==1) Insert(a,b,n-c+1);
else printf("%d\n",n-Query(a,b,c)+1);
}
return 0;
}


Code 2:整体二分

#include<cstdio>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define maxn 50000+5

using namespace std;

struct Seg_Tree{
int l,r,mem;
int tag,sum,sz;
}tr[maxn<<4];

struct Query{
int ct,l,r,c,pos,k;
}q[maxn];

int res[maxn];
int n,m;

bool cmp(const Query &s,const Query &b){
return s.k<b.k;
}

void Update(int k){
tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
}

void Pushdown(int k){
int l=k<<1,r=k<<1|1;
if(tr[k].mem){
tr[l].tag=tr[r].tag=tr[l].sum=tr[r].sum=0;
tr[l].mem=tr[r].mem=1; tr[k].mem=0;
}
if(tr[k].tag){
tr[l].sum+=tr[k].tag*(tr[l].r-tr[l].l+1); tr[r].sum+=tr[k].tag*(tr[r].r-tr[r].l+1);
tr[l].tag+=tr[k].tag; tr[r].tag+=tr[k].tag;
tr[k].tag=0;
}
}

void Build(int l,int r,int k){
tr[k].l=l; tr[k].r=r;
if(l==r) return;
int mid=(l+r)>>1;
Build(l,mid,k<<1);
Build(mid+1,r,k<<1|1);
}

void Add(int l,int r,int val,int k){
Pushdown(k);
if(tr[k].l==l && tr[k].r==r){
tr[k].tag+=val; tr[k].sum+=val*(r-l+1);
return;
}
if(r<=tr[k<<1].r) Add(l,r,val,k<<1);
else if(l>=tr[k<<1|1].l) Add(l,r,val,k<<1|1);
else Add(l,tr[k<<1].r,val,k<<1),Add(tr[k<<1|1].l,r,val,k<<1|1);
Update(k);
}

int Getsum(int l,int r,int k){
Pushdown(k);
if(tr[k].l==l && tr[k].r==r) return tr[k].sum;
if(r<=tr[k<<1].r) return Getsum(l,r,k<<1);
else if(l>=tr[k<<1|1].l) return Getsum(l,r,k<<1|1);
else return Getsum(l,tr[k<<1].r,k<<1)+Getsum(tr[k<<1|1].l,r,k<<1|1);
}

void solve(int l,int r,int x,int y){
if(l==r){
for(int i=x;i<=y;i++) if(q[i].ct==2) res[q[i].pos]=l;
return;
}
int mid=(l+r)>>1,pl=0,pr=y-x+1;
tr[1].mem=1; tr[1].tag=tr[1].sz=tr[1].sum=0;
for(int i=x;i<=y;i++)
if(q[i].ct==1){
if(q[i].c<=mid) q[i].k=++pl;
else{
Add(q[i].l,q[i].r,1,1);
q[i].k=++pr;
}
}
else{
int sum=Getsum(q[i].l,q[i].r,1);
if(sum<q[i].c){
q[i].k=++pl;
q[i].c-=sum;
}
else q[i].k=++pr;
}
sort(q+x,q+y+1,cmp);
solve(l,mid,x,x+pl-1); solve(mid+1,r,x+pl,y);
}

int main(){
freopen("3110.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&q[i].ct,&q[i].l,&q[i].r,&q[i].c);
q[i].pos=i;
}
Build(1,n,1);
solve(0,n,1,m);
for(int i=1;i<=m;i++)
if(res[i]) printf("%d\n",res[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: