zoukankan      html  css  js  c++  java
  • 【bzoj4103】[Thu Summer Camp 2015]异或运算 【可持久化Trie树】

    题意
    给定长度为n的数列X=x1,x2,...,xn和长度为m的数列Y=y1,y2,...,ym,令矩阵A中第i行第j列的值Aij=xi xor yj,每次询问给定矩形区域i[u,d],j[l,r],找出第k大的Aij
    题解
    把求k大变成k小。
    我最初的想法是二分答案mid,然后求有多少个数比mid小,依此来确定答案。观察到询问和第一维很小,我们只需要第一维暴力,第二维建可持久化Trie树就行了。但是这样是两个log的,会TLE,在校内OJ跑了1.7s
    我们考虑怎么把二分去掉,仍然能求k小。
    我们可以拎第一维出来,弄一堆节点一起在可持久化Trie上走。对于每一位,我们算一算有多少个数在这一位异或上这个数第一维上的数(就是异或上xi)为0,记为sum。如果sum<=k,我们就得出了答案这一位为0,然后把那一堆节点往异或xi0的儿子走,递归考虑下一位。否则我们得到了答案这一位为1,然后把那一堆节点往异或xi
    1的儿子走,把k减去sum,递归考虑下一位。这样我们的复杂度就只有一个log了。这其实跟主席树求区间k小的方法很类似。
    在bzoj上跑了6.4s。。讲究
    代码

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1005,M=300005;
    int n,m,q,u,v,l,r,k,tot,A[N],B[N],x[N],y[M],root[M],siz[M*35],ch[M*35][2];
    void build(int y,int &x,int dep,int v){
        x=++tot;
        siz[x]=siz[y]+1;
        ch[x][0]=ch[y][0];
        ch[x][1]=ch[y][1];
        if(dep<0){
            return;
        }
        int md=(v&(1<<dep))>0;
        build(ch[y][md],ch[x][md],dep-1,v);
    }
    int query(int k,int dep){
        if(dep<0){
            return 0;
        }
        int md,sum=0;
        for(int i=u;i<=v;i++){
            md=(x[i]&(1<<dep))>0;
            sum+=siz[ch[B[i]][md]]-siz[ch[A[i]][md]];
        }
        if(k<=sum){
            for(int i=u;i<=v;i++){
                md=(x[i]&(1<<dep))>0;
                A[i]=ch[A[i]][md];
                B[i]=ch[B[i]][md];
            }
            return query(k,dep-1);
        }else{
            for(int i=u;i<=v;i++){
                md=(x[i]&(1<<dep))>0;
                A[i]=ch[A[i]][!md];
                B[i]=ch[B[i]][!md];
            }
            return (1<<dep)+query(k-sum,dep-1);
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&x[i]);
        }
        for(int i=1;i<=m;i++){
            scanf("%d",&y[i]);
            build(root[i-1],root[i],30,y[i]);
        }
        scanf("%d",&q);
        while(q--){
            scanf("%d%d%d%d%d",&u,&v,&l,&r,&k);
            k=(v-u+1)*(r-l+1)-k+1;
            A[0]=B[0]=0;
            for(int i=u;i<=v;i++){
                A[i]=root[l-1];
                B[i]=root[r];
            }
            printf("%d
    ",query(k,30));
        }
        return 0;
    }
  • 相关阅读:
    filter&map&reduce
    Linux通过进程ID查看文件路径
    PyCharm使用最多也最常用默认快捷键介绍
    Python中的深浅拷贝
    类加载器&反射
    Java web.xml 配置详解
    SpringMVC + Spring + MyBatis 整合 + Spring shrio + easyUI + 权限管理框架,带shrio session和shrio cache集群实现方案
    JAVA大数据数组排序
    高访问量WEB开发中的架构模式,学习从点滴开始
    WEB项目会话集群的三种办法
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476842.html
Copyright © 2011-2022 走看看