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; }