• Codeforces 665E. Beautiful Subarrays (字典树)


    题目链接:http://codeforces.com/problemset/problem/665/E

                         (http://www.fjutacm.com/Problem.jsp?pid=2255)

    题意:找出有多少个连续的区间[l,r](1  ≤  l  ≤  r  ≤  n),该区间中所有的数的异或值大于等于k;

    思路:首先,如果是单看题目的话,会发现暴力的话复杂度是O(n^3),但是我们先预处理异或前缀和,然后你会发现[l,r]区间的异或和等于s[l-1]^s[r],这样就可以O(n^2)的求得答案了,但是因为n是1e6,也就是说暴力绝对超时,因为时间才开了3s(虽然说cf里一秒时限,1e10复杂度也可以莽一莽,但是这个是1e12啊!!)。然后,我们会想,因为是异或,而且是要比较,于是乎我们就想到了字典树(不要问为什么,我就是这样想到的)。然后我们可以开始一波分析:

    {

             假设:全是5位二进制(默认补全到5位)

             前提:S[i]是前i个数的异或和,  K为题目里的K,  A是S[i]^A=K 即S[i]^K=A

             S[i] = 5

             K  = 24

             从高位到低位存入字典树时(默认已经存入,现在在find操作):

             {

                       S[i]:00101

                       K : 11000

                      第五位时:k=1,则说明这一位必须往S[i]第五位的异或值方向走,因为我们现在是在查A在字典树上的路径,至于为什么只跑这个数就可以求答案,我们慢慢来。

         第四位同理;

           第三位时:(#敲黑板划重点)这个时候K的第三位为0,按照我说的来看,我们A在树上的路径,那么此时我们要跑的下一步是S[i]的第三位和K第三位的异或值也就是A的第三位,那么如果我下一步不往这个方向走呢(此时A的第三位等于S[i]的第三位)?我往S[i]的第三位异或1的方向跑(反向),那么那个方向延伸的树枝上的所有数字就都是大于K的(这里不做解释),也就是我们虽然不需要跑那里,但是我们要加上经过那个延伸出去的树枝包含的数字个数;

         第二位、第一位同第三位的代码;

                  在跑完所有的情况的时候,我们就求出了所有的A,A满足S[i]^A>k,没错,还少,还少一个等于的情况,此时,我们只需要判断我们是否跑完了5,如果跑完了5位,就加上使用过当前节点的数的个数就得到了答案。

             }

             换一种说法其实就是在字典树上跑A这个数,如果跑到A这个数的某一节点的时候,此时恰好k的这位为0,那么就加上那一位上s[i]^1方向的所有跑过的数字的数量

             Ps:为什么从高位到低位,要是有人不明白就按照上面的方法看看吧!然后你会发现因为高位的值的不确定导致你不能判断。

    }

    于是乎我们就可以开始写代码了。

    Ps:这题的数组大小开到1<<24就好了(本菜鸡之前因为数组开小wa成傻逼)

     1 #include<string.h>
     2 #include<stdio.h>
     3 #include<stdlib.h>
     4 #include<algorithm>
     5 using namespace std;
     6 struct Trie
     7 {
     8     const static int range=2;
     9     const static int maxn=1<<24;///字典树大小
    10     struct node
    11     {
    12         int next[range];
    13         int cnt;///存该节点的经过数字之和
    14     } Trienode[maxn];
    15     int size;
    16     void init()
    17     {
    18         memset(Trienode[0].next,0,sizeof(Trienode[0].next));
    19         Trienode[0].cnt=0;
    20         size=1;
    21     }
    22     void insert(int s)
    23     {
    24         int now=0;
    25         for(int i=30; i>=0; i--)///存31位
    26         {
    27             int c=(s>>i)&1;
    28             if(!Trienode[now].next[c])///没有该节点就开辟
    29             {
    30                 memset(Trienode[size].next,0,sizeof(Trienode[size].next));
    31                 Trienode[size].cnt=0;
    32                 Trienode[now].next[c]=size++;
    33             }
    34             now=Trienode[now].next[c];///往后存
    35             Trienode[now].cnt++;///这个数字经过了该节点,cnt++,因为第一个节点不算,所以说是先往后走再cnt++
    36         }
    37     }
    38     int find(int s, int k)
    39     {
    40         int now=0,i, ans=0;
    41         for(i=30; i>=0; i--)///找30位
    42         {
    43             int c=(s>>i)&1, key=(k>>i)&1;///c:存s[i]的第i位, key:存k的第i位
    44             if(!key&&Trienode[now].next[c^1])///如果key=0的时候,参照上面的分析
    45                 ans+=Trienode[Trienode[now].next[c^1]].cnt;
    46             if(Trienode[now].next[c^key])///往s[i]^K的方向越走越远
    47                 now=Trienode[now].next[c^key];
    48             else
    49                 break;///没路
    50         }
    51         if(i==-1)///跑完30~0的加上最后一位,其他的则是s[i]^K本身不存在
    52             ans+=Trienode[now].cnt;
    53         return ans;
    54     }
    55 } tree;
    56 int ss[1000010];
    57 int main()
    58 {
    59     int n, k;
    60     long long ans=0;///答案会爆long long
    61     tree.init( );
    62     tree.insert(0);///为什么要插入0呢? 因为前i项异或和等于s[i]^0,这样就可以不用特判
    63     ss[0]=0;///因为ss[i]^0=ss[i],用在输入ss[i]时
    64     scanf("%d%d", &n, &k);
    65     for(int i=1; i<=n; i++)
    66     {
    67         scanf("%d", &ss[i]);
    68         ss[i]^=ss[i-1];///处理异或前缀和
    69         ans+=tree.find(ss[i], k);///先找
    70         tree.insert(ss[i]);///再查
    71     }
    72     printf("%I64d
    ", ans);
    73 }
    菜鸡代码
  • 相关阅读:
    [HNOI2004]宠物收养所 题解
    文艺平衡树(区间翻转)(Splay模板)
    搜索专题 题解
    Gorgeous Sequence 题解 (小清新线段树)
    花神游历各国 题解(小清新线段树/树状数组+并查集)
    [HNOI2012]永无乡 题解
    poj 3683 2-sat问题,输出任意一组可行解
    hdu 1824 2-sat问题(判断)
    hdu 4115 石头剪子布(2-sat问题)
    hdu 4421 和poj3678类似二级制操作(2-sat问题)
  • 原文地址:https://www.cnblogs.com/DCD112358/p/8570993.html
Copyright © 2020-2023  润新知