zoukankan      html  css  js  c++  java
  • bzoj 4017: 小Q的无敌异或

    4017: 小Q的无敌异或

    Time Limit: 20 Sec  Memory Limit: 128 MB
    Submit: 593  Solved: 197
    [Submit][Status][Discuss]

    Description

    背景
     
    小Q学习位运算时发现了异或的秘密。
     
    描述
     
    小Q是一个热爱学习的人,他经常去维基百科(http://en.wikipedia.org/wiki/Main_Page)学习计算机科学。
     
    就在刚才,小Q认真地学习了一系列位运算符(http://en.wikipedia.org/wiki/Bitwise_operation),其中按位异或的运算符 xor 对他影响很大。按位异或的运算符是双目运算符。按位异或具有交换律,即i xor j = j xor i。
     
    他发现,按位异或可以理解成被运算的数字的二进制位对应位如果相同,则结果的该位置为0,否则为1,例如1(01) xor 2(10) = 3(11)。
     
    他还发现,按位异或可以理解成数字的每个二进制位进行了不进位的加法,例如3(11) xor 3(11) = 0(00)。
     
    于是他想到了两个关于异或的问题,这两个问题基于一个给定的非负整数序列A1, A2, ..., An,其中n是该序列的长度。
     
    第一个问题是,如果用f(i, j)表示Ai xor Ai+1 xor ... xor Aj,则任意的1 <= i <= j <= n的f(i, j)相加是多少。
     
    第二个问题是,如果用g(i, j)表示Ai + Ai+1 + ... + Aj,则任意的1 <= i <= j <= n的g(i, j)异或在一起是多少。
     
    比如说,对于序列{1, 2},所有的f是{1, 2, 1 xor 2},加起来是6;所有的g是{1, 2, 1 + 2},异或起来是0。
     
    他觉得这两个问题都非常的有趣,所以他找到了你,希望你能快速解决这两个问题,其中第一个问题的答案可能很大,你只需要输出它对998244353(一个质数)取模的值即可。
     

     

    Input

    第一行一个正整数n,表示序列的长度。
     
    第二行n个非负整数A1, A2, ..., An,表示这个序列。
     

    Output

    两个整数,表示两个问题的答案,空格隔开,其中第一个问题的答案要对998244353(一个质数)取模。
     

    Sample Input

    2
    1 2

    Sample Output

    6 0
     

    此题求解该序列所有可能存在的子区间的异或和以及和的异或。
    对于第一问求解区间的异或和。这一问在2017西安现场赛G题出现过,只不过把整个区间改为询问指定的区间。那么我们把每个数拆开,一位一位地来算贡献。在某一位上,我们做一个异或的前缀和,即把+换为^。那我们要求解某个区间[l,r]的异或值,即为sum(r)^sum(l-1)。包含0位置在内共有n+1个端点。我们统计下这个前缀和为1的端点数有k个,那么为0的就有(n+1-k)个,那么区间俩端点必须由不同数组成异或才为1(即sum(r)和sum(l-1)),这样的区间我们能选择k*(n+1-k),这即为第一问答案。
    对于第二问。我们依旧拆开一位一位做。假如这是第k位,我们做%(2k+1)的前缀和。
    如果第k位为1 ,那么(sum(r)sum(l1))mo2k+12k,这点显而易见。
    用ans存这位是否为1,。我们从小到大枚举r,看满足这个等式的l-1是否为奇数个,如果是则ans^=1,否则不管。
    但这东西不拆开来是不好做的。这个式子拆开 mod前的数并变换可以得出两个式子来限定sum(l-1)%(2k+1)的取值范围:
    1. sum(l1)mo2k+1(sum(r)mod2k+1)20sum(l1)mod2k+1 ,即 0sum(l1)mod2k+1(sum(r)mod2k+1)2k
    2. sum(l1)mod2k+1(sum(r)mod2k+1)2k+2k+1=(sum(r)mod2k+1)+2,sum(r)mod2k+1<sum(l1)mod2k+,即 sum(r)mod2k+1<sum(l1)mod2k+1(sum(r)mod2k+1)+2

    我们先离散化所有前缀和的值。在枚举r的过程中,然后看对应区间内的数量是不是奇数个,这个可以用^的树状数组实现。或者权值线段树实现。

    这样枚举了右端点后,就能得出ans,若为1则加上相应的2的幂次方作为答案贡献。

     1 #include<bits/stdc++.h>
     2 #define clr(x) memset(x,0,sizeof(x))
     3 #define clr_1(x) memset(x,-1,sizeof(x))
     4 #define LL long long
     5 #define mod 998244353
     6 using namespace std;
     7 const int N=1e5+10;
     8 int bit[N];
     9 LL a[N];
    10 LL order[N],found[N];
    11 int n,m,k,cnt;
    12 LL ans1,ans2;
    13 void qy1(int mp)
    14 {
    15     LL num=0;
    16     LL now=0;
    17     for(int i=1;i<=n;i++)
    18     {
    19         now^=(a[i]>>mp)&1;
    20         if(now)
    21             num++;
    22     }
    23     ans1=(ans1+num*(n-num+1)%mod*(1LL<<mp)%mod)%mod;
    24     return ;
    25 }
    26 void add(int i,int x)
    27 {
    28     if(!i) return ;
    29     while(i<=cnt+1)
    30     {
    31         bit[i]^=x;
    32         i+= i&-i;
    33     }
    34     return ;
    35 }
    36 int sum(int i)
    37 {
    38     int res=0;
    39     while(i)
    40     {
    41         res^=bit[i];
    42         i-= i&-i;
    43     }
    44     return res;
    45 }
    46 void qy2(int mp)
    47 {
    48     clr(bit);
    49     int p;
    50     found[0]=order[0]=0;
    51     for(int i=1;i<=n;i++)
    52     {
    53         found[i]=(found[i-1]+a[i])%(1LL<<(mp+1));
    54         order[i]=found[i];
    55     }
    56     sort(order,order+n+1);
    57     cnt=unique(order,order+n+1)-order-1;
    58     int ans=0;
    59     for(int i=0;i<=n;i++)
    60     {
    61         p=lower_bound(order,order+cnt+1,found[i])-order;
    62         if(order[p]!=found[i]) p--;
    63         p++;
    64         add(p,1);
    65         ans^=sum(p);
    66         p=lower_bound(order,order+cnt+1,found[i]-(1LL<<mp))-order;
    67         if(order[p]!=found[i]-(1LL<<mp)) p--;
    68         p++;
    69         ans^=sum(p);
    70         p=lower_bound(order,order+cnt+1,found[i]+(1LL<<mp))-order;
    71         if(p==cnt+1 || order[p]!=found[i]+(1LL<<mp)) p--;
    72         p++;
    73         ans^=sum(p);
    74     }
    75     if(ans) ans2|=(1LL<<mp);
    76     return ;
    77 }
    78 int main()
    79 {
    80     scanf("%d",&n);
    81     for(int i=1;i<=n;i++)
    82         scanf("%lld",&a[i]);
    83     ans1=ans2=0;
    84     for(int i=0;i<=20;i++) qy1(i);
    85     for(int i=0;i<=40;i++) qy2(i);
    86     printf("%lld %lld
    ",ans1,ans2);
    87     return 0;
    88 }
    View Code
  • 相关阅读:
    Alpha 冲刺 (8/10)
    Alpha 冲刺 (7/10)
    Alpha 冲刺 (6/10)
    团队作业-随堂小测(同学录)
    Alpha 冲刺 (5/10)
    LeetCode-1
    c++向量
    软件工程实践总结作业
    个人作业——软件产品案例分析
    个人技术博客Alpha----Android Studio学习
  • 原文地址:https://www.cnblogs.com/wujiechao/p/7764678.html
Copyright © 2011-2022 走看看