首页 > 技术文章 > BZOJ3110: [Zjoi2013]K大数查询

Yangrui-Blog 2018-07-15 21:58 原文

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

Sample Output

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

 

推荐阅读