• 异或运算


    1702:异或运算

    时间限制: 2000 ms         内存限制: 524288 KB

    【题目描述】

    给定长度为n的数列X={x1,x2,...,xn}和长度为m的数列Y={y1,y2,...,ym},令矩阵A中第i行第j列的值Aij=xi^yj,每次询问给定矩形区域i∈[u,d],j∈[l,r],找出第k大的Aij。

    【输入】

    第一行包含两个正整数n,m,分别表示两个数列的长度;

    第二行包含n个非负整数xi;

    第三行包含m个非负整数yi;

    第四行包含一个正整数p,表示询问次数;

    随后p行,每行均包含5个正整数,用来描述一次询问,每行包含五个正整数u,d,l,r,k,含义如题意所述。

    【输出】

    共p行,每行包含一个非负整数,表示此次询问的答案。
    【输入样例】

    3 3
    1 2 4
    7 6 5
    3
    1 2 1 2 2
    1 2 1 3 4
    2 3 2 3 4

    【输出样例】

    6
    5
    1

    【提示】

    【数据规模】

    对于100%的数据:

    0≤Xi,Yj<pow(2,31),1≤u≤d≤n≤1000,1≤l≤r≤m≤300000,1≤k≤(d-u+1)×(r-l+1),1≤p≤500。

    【题解】

    首先考虑到枚举数的每一位,如果可以填1就这一位填1否则填0。

    可以构造可持久化trie树,按位拆分加入每一个Y数组中元素。

    查询:

    因为p和n均很小,可以考虑枚举每一位A中元素。

    查询时算出这一位取1时可以构造出好多数,如果>k则取1,否则取0,k-=个数。再将A每一位所对应的trie节点更新为其左或右儿子,即可 查出答案。

    复杂度O(31*(n*p+m)).

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int M=3e5+5;
    int n,m,root[M],a[1005],cnt,p,pr[1005],no[1005],ans;
    struct trie
    {
        int er[2],siz;
    }sh[M*33];
    inline int read()
    {
        char c=getchar();
        int x=0,f=1;
        while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
        return x*f;
    }
    inline void pushup(int now)
    {
        sh[now].siz=sh[sh[now].er[0]].siz+sh[sh[now].er[1]].siz;
    }
    inline int insert(int now,int pre,int x,int ge)
    {
        now=++cnt;sh[now]=sh[pre];
        if(ge==-1)
        {
            sh[now].siz++;
            return now;
        }
        
        int hu=(x>>ge)&1;
        sh[now].er[hu]=insert(sh[now].er[hu],sh[pre].er[hu],x,ge-1);
        pushup(now);
        return now;
    }
    inline void query(int ge,int l,int r,int k)
    {
        int gu=0,pan=(1<<ge);
        if(ge==-1) return;
        for(int i=l;i<=r;i++)
        {
            if(a[i]&pan) gu+=sh[sh[no[i]].er[0]].siz-sh[sh[pr[i]].er[0]].siz;
            else gu+=sh[sh[no[i]].er[1]].siz-sh[sh[pr[i]].er[1]].siz;
        }
        if(gu>=k)
        {
            for(int i=l;i<=r;i++)
            {
                if(a[i]&pan) no[i]=sh[no[i]].er[0],pr[i]=sh[pr[i]].er[0];
                else no[i]=sh[no[i]].er[1],pr[i]=sh[pr[i]].er[1];
            }
            ans|=pan;
            query(ge-1,l,r,k);
        }
        else
        {
            for(int i=l;i<=r;i++)
            {
                if(a[i]&pan) no[i]=sh[no[i]].er[1],pr[i]=sh[pr[i]].er[1];
                else no[i]=sh[no[i]].er[0],pr[i]=sh[pr[i]].er[0];
            }
            query(ge-1,l,r,k-gu);
        }
    }
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=1,x;i<=m;i++)
        {
            x=read();
            root[i]=insert(root[i],root[i-1],x,30);
        }
        p=read();
        for(int i=1,x,y,z,w,u;i<=p;i++)
        {
            x=read();y=read();z=read();w=read();u=read();
            for(int j=x;j<=y;j++) pr[j]=root[z-1],no[j]=root[w];//极角米巴游
            ans=0;
            query(30,x,y,u); 
            cout<<ans<<"
    ";
        }
        return 0;
    }
  • 相关阅读:
    LeetCode链表解题模板
    c++中的new、operator new、placement new
    树的前序、中序、后续、层次遍历的递归和非递归解法
    c++Volatile关键词
    南大算法设计与分析课程OJ答案代码(5)--割点与桥和任务调度问题
    c++右值引用以及使用
    c++选择重载函数
    从4行代码看右值引用
    被遗忘的C结构体打包技术
    南大算法设计与分析课程OJ答案代码(4)--变位词、三数之和
  • 原文地址:https://www.cnblogs.com/betablewaloot/p/12146931.html
Copyright © 2020-2023  润新知