• BZOJ3110: [Zjoi2013]K大数查询


    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
    ",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
    ",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;
    }
    
  • 相关阅读:
    【java】一维数组循环位移方阵
    【java】for循环输出数字金字塔
    C++编程tips
    C++中cin.get 和cin.peek 及其相关的用法
    ubuntu增加字符设备驱动程序/ 操作系统课程设计
    C++ Primer 学习笔记/ 处理类型
    C++学习,顶层const
    C++学习笔记/const和指针
    ubuntu16.04增加系统调用(拷贝)
    Java学习笔记#数组 循环遍历
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9315174.html
Copyright © 2020-2023  润新知