BZOJ3110: [Zjoi2013]K大数查询
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
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 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
1
2
1
2
1
HINT
【样例说明】
第一个操作 后位置 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<=long long
题解Here!
刚学的树套树,就遇上了这题。
然后我不知死活地一发线段树Splay,完美地TLE了。。。
附上炒鸡长的代码纪念:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 20000010
#define MAXM 50010
using namespace std;
const long long maxn=50000;
int n,m;
namespace BST{
int size=1,root[MAXM<<2];
struct Splay{
int son[2];
int f,v,s,flag;
}a[MAXN];
inline void clean(int rt){
a[rt].son[0]=a[rt].son[1]=a[rt].f=a[rt].s=a[rt].v=a[rt].flag=0;
}
inline void pushup(int rt){
if(!rt)return;
a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+a[rt].flag;
}
inline void turn(int rt,int k){
int x=a[rt].f,y=a[x].f;
a[x].son[k^1]=a[rt].son[k];
if(a[rt].son[k])a[a[rt].son[k]].f=x;
a[rt].f=y;
if(y)a[y].son[a[y].son[1]==x]=rt;
a[x].f=rt;
a[rt].son[k]=x;
pushup(x);pushup(rt);
}
void splay(int rt,int ancestry,int i){
while(a[rt].f!=ancestry){
int x=a[rt].f,y=a[x].f;
if(y==ancestry)turn(rt,a[x].son[0]==rt);
else{
int k=a[y].son[0]==x?1:0;
if(a[x].son[k]==rt){turn(rt,k^1);turn(rt,k);}
else{turn(x,k);turn(rt,k);}
}
}
if(ancestry==0)root[i]=rt;
}
void insert(int i,int x,int t){
int rt=root[i];
if(!rt){
root[i]=rt=size++;
a[rt].son[0]=a[rt].son[1]=a[rt].f=0;
a[rt].v=x;a[rt].s=a[rt].flag=t;
return;
}
int fa=0;
while(1){
if(a[rt].v==x){
a[rt].flag+=t;
pushup(rt);pushup(fa);
break;
}
fa=rt;
rt=a[rt].son[a[rt].v<x];
if(!rt){
rt=size++;
a[rt].v=x;a[rt].flag=a[rt].s=t;
a[rt].f=fa;a[fa].son[a[fa].v<x]=rt;
a[rt].son[0]=a[rt].son[1]=0;
pushup(fa);
break;
}
}
splay(rt,0,i);
}
int rank(int i,int x){
int rt=root[i],s=0;
while(rt){
if(a[rt].v==x)return s+a[a[rt].son[0]].s;
else if(a[rt].v<x){
s+=a[a[rt].son[0]].s+a[rt].flag;
rt=a[rt].son[1];
}
else rt=a[rt].son[0];
}
return s;
}
long long get_size(int i){
int rt=root[i];
return (long long)a[rt].s;
}
}
namespace ST{
#define LSON rt<<1
#define RSON rt<<1|1
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
struct Segment_Tree{
int l,r;
}a[MAXM<<2];
void buildtree(int l,int r,int rt){
int mid;
LSIDE(rt)=l;
RSIDE(rt)=r;
if(l==r)return;
mid=l+r>>1;
buildtree(l,mid,LSON);
buildtree(mid+1,r,RSON);
}
void insert(int l,int r,int c,int rt){
int mid;
BST::insert(rt,c,min(RSIDE(rt),r)-max(LSIDE(rt),l)+1);
if(LSIDE(rt)==RSIDE(rt))return;
mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid)insert(l,r,c,LSON);
if(mid<r)insert(l,r,c,RSON);
}
int query_rank(int l,int r,int c,int rt){
int mid,ans=0;
if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return BST::rank(rt,c);
mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid)ans+=query_rank(l,r,c,LSON);
if(mid<r)ans+=query_rank(l,r,c,RSON);
return ans;
}
long long query_size(int l,int r,int rt){
int mid,ans=0;
if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return BST::get_size(rt);
mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid)ans+=query_size(l,r,LSON);
if(mid<r)ans+=query_size(l,r,RSON);
return ans;
}
int get_kth(int l,int r,int k){
int left=-maxn-1,right=maxn+1,mid,s;
while(left<=right){
mid=left+right>>1;
s=query_rank(l,r,mid,1);
if(s<k)left=mid+1;
else right=mid-1;
}
return left-1;
}
}
inline long long read(){
long long date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
void work(){
int f,x,y;
long long k;
while(m--){
f=read();x=read();y=read();k=read();
if(f==1)ST::insert(x,y,k,1);
if(f==2){
k=ST::query_size(x,y,1)-k+1;
printf("%d\n",ST::get_kth(x,y,k));
}
}
}
void init(){
n=read();m=read();
ST::buildtree(1,n,1);
}
int main(){
init();
work();
return 0;
}
正解也是树套树,然而是权值线段树套动态开点区间线段树。。。
(蒟蒻表示并不会整体二分/cdq分治。。。药丸。。。)
外层是权值线段树,实质上就是一个二分答案的过程。
然后运用类似归并树和主席树的思想,我们设询问区间为 [ql,qr] ,答案区间为 [l,r] ,取其 midmid ,然后计算在左儿子 [l,mid] 和询问区间 [ql,qr] 中的数的个数。
那么我们就可以判断答案是大于 mid 还是小于 mid 的了。
而在权值线段树的每一个节点上都建一个区间线段树,来维护该权值在 [1,n] 所有区间上的出现次数,然后维护一个区间和就好了。
记得离散化。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 20000010
#define MAXM 50010
using namespace std;
int n,m,K;
int root[MAXM<<2],b[MAXM];
struct Question{
int f,l,r;
long long k;
}que[MAXM];
namespace CT{
long long size=0;
struct Chairman_Tree{
int l,r;
long long sum,size;
}a[MAXN];
void insert(int left,int right,int l,int r,int &rt){
if(!rt)rt=++size;
a[rt].sum+=min(right,r)-max(left,l)+1;
if(left<=l&&r<=right){
a[rt].size++;
return;
}
int mid=l+r>>1;
if(left<=mid)insert(left,right,l,mid,a[rt].l);
if(mid<right)insert(left,right,mid+1,r,a[rt].r);
}
long long query(int left,int right,int l,int r,int rt){
if(!rt)return 0;
if(left<=l&&r<=right)return a[rt].sum;
int mid=l+r>>1;
long long ans=a[rt].size*(min(right,r)-max(left,l)+1);
if(left<=mid)ans+=query(left,right,l,mid,a[rt].l);
if(mid<right)ans+=query(left,right,mid+1,r,a[rt].r);
return ans;
}
}
namespace ST{
#define LSON rt<<1
#define RSON rt<<1|1
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
struct Segment_Tree{
int l,r;
}a[MAXM<<2];
void buildtree(int l,int r,int rt){
int mid;
LSIDE(rt)=l;
RSIDE(rt)=r;
if(l==r)return;
mid=l+r>>1;
buildtree(l,mid,LSON);
buildtree(mid+1,r,RSON);
}
void update(int l,int r,int c,int rt){
int mid;
CT::insert(l,r,1,n,root[rt]);
if(LSIDE(rt)==RSIDE(rt))return;
mid=LSIDE(rt)+RSIDE(rt)>>1;
if(c<=mid)update(l,r,c,LSON);
else update(l,r,c,RSON);
}
long long query(int l,int r,long long c,int rt){
if(LSIDE(rt)==RSIDE(rt))return LSIDE(rt);
int mid=LSIDE(rt)+RSIDE(rt)>>1;
long long t=CT::query(l,r,1,n,root[LSON]);
if(c<=t)return query(l,r,c,LSON);
else return query(l,r,c-t,RSON);
}
}
inline long long read(){
long long date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
void work(){
int f,x,y;
long long k;
for(int cases=1;cases<=m;cases++){
f=que[cases].f;x=que[cases].l;y=que[cases].r;k=que[cases].k;
if(f==1)ST::update(x,y,K-k+1,1);
if(f==2)printf("%d\n",b[K-ST::query(x,y,k,1)+1]);
}
}
void init(){
n=read();m=read();
for(int i=1;i<=m;i++){
que[i].f=read();que[i].l=read();que[i].r=read();que[i].k=read();
if(que[i].f==1)b[++K]=que[i].k;
}
sort(b+1,b+K+1);
K=unique(b+1,b+K+1)-b-1;
for(int i=1;i<=m;i++)if(que[i].f==1)que[i].k=lower_bound(b+1,b+K+1,que[i].k)-b;
ST::buildtree(1,K,1);
}
int main(){
init();
work();
return 0;
}

浙公网安备 33010602011771号