• 【BZOJ4103】异或运算(THUSC2015)-可持久化trie树+位运算


    测试地址:异或运算
    题目大意:给定两个数列XY,分别包含NM个非负整数,其中N1000M300000,有P个询问,P500,每个询问给出5个参数u,d,l,r,k,意为求所有满足uid,ljr的数XixorYj中第k大的数。
    做法:因为要去参加THUSC2017了,所以就想来做一下历年的题,结果……看到题目就蒙圈了,什么鬼?看了题解才知道是还没有学习的东西,于是紧赶慢赶一个小时把这个新东西学了。
    这题的正确做法是可持久化trie树,也就是可持久化字典树。
    初看这题,如果暴力求出所有异或出来的值,然后整体二分的话,总复杂度应为O(NMlog(NM)),显然是不行的。
    注意到P不大,那么我们就不要拘泥于离线算法,而去寻求一个在线算法。我们可以基于以下步骤寻找第k大数:从高到低枚举每一个二进制位,对于一个二进制位,通过统计确定这个二进制位是填0还是填1,具体来说就是,如果这个位置上为1,合法的数字数量k,那么这个位置就应该填1,否则就填0。可以看出这很像是splay中查找第k大数的方法,只不过是基于二进制位查找,那么我们实际上要解决的就是统计上文中“合法的数字”的数量。注意到每一个二进制数可以表示成一个01串,那么我们可以把问题转化成求拥有某个前缀的字符串数量,很容易看出这个问题用一棵trie树就可以解决。
    然而还要考虑子矩阵的限制。发现N比较小,所以我们枚举这一维,然后我们要求某个区间内拥有某个前缀的字符串数量,我们就可以仿造可持久化线段树的建树方式建造一棵可持久化trie树,使其满足一种前缀和的性质,这样的话每一次查询的复杂度就是O(31)
    再来理一下整个解题的过程:用O(31M)的复杂度以数列Y建一棵可持久化trie树,然后再处理询问,最外层枚举二进制位,然后用一些指针来实现可持久化trie树上的查找,那么处理询问的总复杂度为O(31PN),整道题就这样解决了。具体可以看本人的代码。
    犯二的地方:空间复杂度算错导致开小了数组,RE了两次,下次要注意…
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,q,rt[300010]={0},sum[9600010]={0},ch[9600010][2]={0},tot=0;
    int nowl[1010],nowr[1010],bt[32];
    bool x[1010][32]={0};
    
    void insert(int last,int &no,int v,int d)
    {
      no=++tot;
      sum[no]=sum[last],ch[no][0]=ch[last][0],ch[no][1]=ch[last][1];
      if (d<0) {sum[no]++;return;}
      if (v&bt[d]) insert(ch[last][1],ch[no][1],v,d-1);
      else insert(ch[last][0],ch[no][0],v,d-1);
      sum[no]=sum[ch[no][0]]+sum[ch[no][1]];
    }
    
    int query(int u,int d,int l,int r,int k)
    {
      for(int i=u;i<=d;i++)
        nowl[i]=rt[l-1],nowr[i]=rt[r];
      int ans=0,s;
      for(int j=30;j>=0;j--)
      {
        s=0;
        for(int i=u;i<=d;i++)
          s+=sum[ch[nowr[i]][!x[i][j]]]-sum[ch[nowl[i]][!x[i][j]]];
        if (s>=k)
        {
          for(int i=u;i<=d;i++)
          {
            nowl[i]=ch[nowl[i]][!x[i][j]];
            nowr[i]=ch[nowr[i]][!x[i][j]];
          }
          ans+=bt[j];
        }
        else
        {
          for(int i=u;i<=d;i++)
          {
            nowl[i]=ch[nowl[i]][x[i][j]];
            nowr[i]=ch[nowr[i]][x[i][j]];
          }
          k-=s;
        }
      }
      return ans;
    }
    
    int main()
    {
      scanf("%d%d",&n,&m);
      for(int i=1;i<=n;i++)
      {
        int a,j=0;
        scanf("%d",&a);
        while(a)
        {
          x[i][j]=a&1;
          a>>=1;j++;
        }
      }
      bt[0]=1;
      for(int i=1;i<=30;i++) bt[i]=bt[i-1]<<1;
      for(int i=1;i<=m;i++)
      {
        int a;
        scanf("%d",&a);
        insert(rt[i-1],rt[i],a,30);
      }
    
      scanf("%d",&q);
      for(int i=1;i<=q;i++)
      {
        int u,d,l,r,k;
        scanf("%d%d%d%d%d",&u,&d,&l,&r,&k);
        printf("%d
    ",query(u,d,l,r,k));
      }
    
      return 0;
    }
    
  • 相关阅读:
    Java基础之IO流,使用File类以树形结构打印指定文件目录
    Java基础之IO流,以字节流的方式操作读写文件FileOutputStream和FileInputStream的使用
    Java基础之IO流,自定义字节流缓冲区装饰类(模仿)
    Java基础之IO流,转换流应用InputStreamReader,OutputStreamWriter
    碰撞处理游戏的原型
    flash中物体运动基础之六方向与角度
    flash中物体运动基础之七碰撞处理
    推导坐标旋转公式
    flash中物体运动基础之一匀速运动
    flash中物体运动基础之五障碍物
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793690.html
Copyright © 2020-2023  润新知