• BZOJ 4103 [Thusc 2015]异或运算 (可持久化01Trie+二分)


    题目大意:给你一个长方形矩阵,位置$i,j$上的数是$a_{i};xor;b_{j}$,求某个子矩阵内第$K$大的值

    最先想的是二分答案然后验证,然而是$O(qnlogmloga_{i})$,不出意外会被卡..看完题解才恍然大悟

    $01Trie$是具有二分性质的!因为每个节点最多有2个儿子!

    先对$b$序列建可持久化$01Trie$,记录一个$sum$表示当前节点的子树内有多少个数

    对于每次询问,因为$n$很小,暴力枚举$a$进行统计,记录每个a当前在01Trie的位置

    接下来就是在$01Trie$上二分了

    按位从高到低枚举,统计一共有多少个数这一位是1,即每个a所在$01Trie$位置 和这一位异或值为1 的子树内,记为$tot$

    如果$tot>k$,说明这一位不是1,每个$a$分别向异或值是0的地方走,然后$K-=tot$,去掉这一位填1的贡献

    反之每个$a$往异或值为1的地方走

    最后输出答案即可

    #include <set>
    #include <queue>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N1 301000
    #define N2 10201000
    #define MM 100
    #define ll long long
    #define dd double  
    #define uint unsigned int
    #define mod 1000000007
    #define idx(X) (X-'a')
    #define it multiset<node>::iterator
    using namespace std;
    
    int gint()
    {
        int ret=0,fh=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
        while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
        return ret*fh;
    }
    
    uint bin[35];
    int n,m;
    uint a[N1],b[N1],pa[N1];
    int px[N1],py[N1];
    
    struct Trie{
    int ch[N2][2],num[N2],root[N1],tot;
    int sum[N2],stk[N2],tp;
    void init()
    {
        root[0]=tot=1;int x=1;
        for(int i=30;i>=0;i--){
            ch[x][0]=++tot;
            x=ch[x][0],num[x]=1;
        }
    }
    void insert(int s,int rt1,int rt2,int w)
    {
        int x,y,p;
        y=root[rt1];
        x=root[rt2]=++tot;
        for(int i=31;i>=0;i--){
            p=(s&bin[i])?1:0;
            ch[x][p]=++tot;
            ch[x][p^1]=ch[y][p^1];
            num[ch[x][p]]=num[ch[y][p]]+w;
            sum[ch[x][p]]=sum[ch[y][p]];
            x=ch[x][p],y=ch[y][p];
            stk[++tp]=x;
        }
        sum[x]++,stk[tp--]=0;
        while(tp){
            x=stk[tp--];
            sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
        }
    }
    uint query(int L,int R,int l,int r,int K)
    {
        int x,y,p;uint ans=0;
        y=l<0?0:root[l],x=root[r];
        int s[2],tot;
        for(int j=L;j<=R;j++)
            px[j]=x,py[j]=y;
        for(int i=31;i>=0;i--)
        {
            s[0]=0,s[1]=0,tot=0;
            for(int j=L;j<=R;j++){
                p=(a[j]&bin[i])?1:0,s[p]++;
                tot+=sum[ch[px[j]][p^1]]-sum[ch[py[j]][p^1]];
            }
            if(K>tot){
                K-=tot;
                for(int j=L;j<=R;j++){
                    p=(a[j]&bin[i])?1:0;
                    if(num[ch[px[j]][p]]-num[ch[py[j]][p]]>0)
                        px[j]=ch[px[j]][p],py[j]=ch[py[j]][p];
                    else px[j]=py[j]=0;
                }
            }else{
                for(int j=L;j<=R;j++){
                    p=(a[j]&bin[i])?1:0;
                    if(num[ch[px[j]][p^1]]-num[ch[py[j]][p^1]]>0)
                        px[j]=ch[px[j]][p^1],py[j]=ch[py[j]][p^1];
                    else px[j]=py[j]=0;
                }ans|=bin[i];
            }
        }return ans;
    }
    }T;
    
    int main()
    {
        //freopen("t1.in","r",stdin);
        scanf("%d%d",&n,&m);
        for(int i=31;i>=0;i--)
            bin[i]=(1<<i);
        for(int i=1;i<=n;i++)
            a[i]=gint();
        T.init();
        for(int i=1;i<=m;i++)
            b[i]=gint(),T.insert(b[i],i-1,i,1);
        int Q,u,d,l,r,K;
        scanf("%d",&Q);
        for(int q=1;q<=Q;q++)
        {
            u=gint(),d=gint(),l=gint(),r=gint(),K=gint();
            printf("%d
    ",T.query(u,d,l-1,r,K));
        }
        return 0;
    }
  • 相关阅读:
    Facade
    Adapter
    Bridge
    Factory
    Singleton
    Decorator
    Template Method
    设计模式
    寻找最小的k个数
    java并发编程(4)--线程池的使用
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10031560.html
Copyright © 2020-2023  润新知