zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    IOS Charles(代理服务器软件,可以用来拦截网络请求)
    Javascript中addEventListener和attachEvent的区别
    MVC中实现Area几种方法
    Entity Framework Code First 中使用 Fluent API 笔记。
    自定义JsonResult解决 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    An entity object cannot be referenced by multiple instances of IEntityChangeTracker 的解决方案
    Code First :使用Entity. Framework编程(8) ----转发 收藏
    Code First :使用Entity. Framework编程(6) ----转发 收藏
    Code First :使用Entity. Framework编程(5) ----转发 收藏
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793691.html
Copyright © 2011-2022 走看看