您的位置:首页 > 其它

bzoj3110[ZJOI2013]K大数查询 树套树

2017-02-12 15:30 218 查看
题目描述

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

输入输出格式

输入格式:

第一行N,M接下来M行,每行形如1 a b c或2 a b c

输出格式:

输出每个询问的结果

输入输出样例

输入样例#1:

2 5

1 1 2 1

1 1 2 2

2 1 1 2

2 1 1 1

2 1 2 3

输出样例#1:

1

2

1

说明

【样例说明】

第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1的数有 1 、2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3大的数是 1 。;

N,M<=50000,N,M<=50000

a<=b<=N

1操作中abs(c)<=N

2操作中c<=Maxlongint

分析:

1.总体的思路可以是整体二分,权值线段树套区间线段树,线段树套Splay,线段树套主席树等,我用的是树套树中的权值线段树的做法

2.操作1中abs(c)<=N,因此我们可以用类似主席树的表示方法,在外层建立一颗权值线段树,每个节点表示一个权值区间的数的个数

3.而对于每一个节点,再建立一颗线段树来维护区间。

4.那么1操作就是权值线段树单点修改,区间线段树区间修改;2操作就是权值线段树区间查询,区间线段树区间查询

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
const int maxn=50010;
const int maxm=12000010;
struct node{
int op;
int a,b;
LL c;
}Q[maxn];
int ql,qr;
int n,m;
map<LL,int>mp;
int num[maxn],val[maxn];
int node;
int lc[maxm],rc[maxm];
LL sum[maxm],add[maxm];
int root[maxn<<4];
void modify(int &o,int l,int r){
if(!o) o=++node;
int mid=(l+r)>>1;
if(ql<=l && qr>=r){add[o]++;sum[o]+=(r-l+1);return;}
if(ql<=mid) modify(lc[o],l,mid);
if(qr>mid) modify(rc[o],mid+1,r);
sum[o]=sum[lc[o]]+sum[rc[o]]+add[o]*(r-l+1);
}
LL query(int &o,int l,int r,LL addv){
if(!o) o=++node;
int mid=(l+r)>>1;
if(ql<=l && qr>=r) return sum[o]+addv*(r-l+1);
LL ret=0;
if(ql<=mid) ret+=query(lc[o],l,mid,addv+add[o]);
if(qr>mid) ret+=query(rc[o],mid+1,r,addv+add[o]);
return ret;
}
int cnt;
int main(){
scanf("%d%d",&n,&m);
int L,R,now;
for(int i=1;i<=m;i++){
scanf("%d%d%d%lld",&Q[i].op,&Q[i].a,&Q[i].b,&Q[i].c);
if(Q[i].op==1) num[++cnt]=Q[i].c;
}
sort(num+1,num+1+cnt);
int nm=0;
for(int i=1;i<=m;i++) if(i==1 || num[i]!=num[i-1]) mp[num[i]]=++nm,val[nm]=num[i];
LL c;
for(int i=1;i<=m;i++){
ql=Q[i].a,qr=Q[i].b;
now=1,L=1,R=n;
c=Q[i].c;
if(Q[i].op==1){
c=mp[c];
while(L<R){
modify(root[now],1,n);
int mid=(L+R)>>1;
if(c<=mid) R=mid,now=now<<1;
else L=mid+1,now=now<<1|1;
}
modify(root[now],1,n);
}else{
do{
LL t=query(root[now<<1|1],1,n,0);
int mid=(L+R)>>1;
if(t>=c) L=mid+1,now=now<<1|1;
else R=mid,now=now<<1,c-=t;
}while(L<R);
printf("%d\n",val[L]);
}
}
return 0;
}


^_^
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: