• P3760 [TJOI2017]异或和


    题目描述

    在加里敦中学的小明最近爱上了数学竞赛,很多数学竞赛的题都是与序列的连续和相关的。所以对于一个序列,求出它们所有的连续和来说,小明觉得十分的简单。但今天小明遇到了一个序列和的难题,这个题目不仅要求你快速的求出所有的连续和,还要快速的求出这些连续和的异或值。小明很快的就求出了所有的连续和,但是小明要考考你,在不告诉连续和的情况下,让你快速求是序列所有连续和的异或值。

    输入输出格式

    输入格式:

     

    第一行输入一个n,表示这序列的数序列 第二行输入n个数字a1,a2...an代表这个序列

    0<=a1,a2,...an,0<=a1+a2...+an<=10^6

     

    输出格式:

     

    输出这个序列所有的连续和的异或值

     

    输入输出样例

    输入样例#1: 
    3
    1 2 3
    输出样例#1: 
    0

    说明

    【样例解释】

    序列1 2 3有6个连续和,它们分别是1 2 3 3 5 6,则1 xor 2 xor 3 xor 3 xor 5 xor 6 = 0

    【数据范围】

    对于20%的数据,1<=n<=100

    对于100%的数据,1<=n <= 10^5

    Solution:

      noip2018结束了,省一稳了,但分数真的很不满意,WC和省选都是奢望了,我应该也就此AFO了吧!游记什么的等分数线出来了再抽空补上(实际是想以noip2018游记作为最后一篇我关于OI的博客),先得把前段时间做的一些题目补上。

      本题非常巧妙的位运算+树状数组。

      我们按位来统计,若某一位为$1$的子段和个数为奇数个,则答案累加该位的位权。那么问题变为如何快速统计某一位为$1$的子段和有多少个。

      我们对原数组先作前缀和$s$,那么对于$s_i$,我们统计以$i$为结尾的当前位为$1$的子段和有多少个,这里可以用两个树状数组分别维护当前位为$0$和$1$的个数,来辅助统计。

      具体来说,若$s_i$的当前位为$1$,那么我们要找一个$j,j<i$使得$s_i-s_j$的当前位为$1$,需要统计的就是满足条件的$j$的个数,由于$s_i$的当前位为$1$,显然满足条件的$s_j$有两种情况:1、$s_j$的当前位为$1$且$s_j$的当前位之前的位的$01$值大于$s_i$的当前位之前的位的$01$值(这样用$s_i-s_j$就需要借位使得减得的值当前位为$1$); 2、$s_j$的当前位为$0$且$s_j$的当前位之前的位的$01$值小于等于$s_i$的当前位之前的$01$值(这样用$s_i-s_j$就直接会使减得的值当前位为$1$)。于是统计时只需要累加维护$0$和维护$1$的两个树状数组满足条件的$s_j$个数,然后把$s_i$的当前位之前的位的值按当前位为$0$或$1$插入对应的树状数组中就好了。同理可以去考虑$s_i$当前位为$0$的情况。

      注意细节:树状数组统计不能从$0$开始(受lowbit限制),所以得整体右移一位,最开始的时候还得先插入一个$s_0=0$!

    代码:

    /*Code by 520 -- 10.30*/
    #include<bits/stdc++.h>
    #define il inline
    #define ll long long
    #define RE register
    #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
    #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
    using namespace std;
    const int N=1e6+5;
    int n,s[N],c[2][N],ans;
    
    il void update(int tag,int x){while(x<N)c[tag][x]++,x+=x&(-x);}
    
    il int query(int tag,int x){int res=0;while(x)res+=c[tag][x],x-=x&(-x);return res;}
    
    int main(){
        scanf("%d",&n);
        For(i,1,n) scanf("%d",s+i),s[i]+=s[i-1];
        For(i,0,20) {
            int lim=(1<<i)-1,pos; ll tot=0;
            if(lim>s[n]) break;
            memset(c,0,sizeof(c));
            update(0,1);
            For(j,1,n){
                pos=(s[j]&lim)+1;
                if(s[j]&(1<<i)) 
                    tot+=query(0,pos)+query(1,1000001)-query(1,pos),update(1,pos);
                else 
                    tot+=query(1,pos)+query(0,1000001)-query(0,pos),update(0,pos);
            }
            if(tot&1) ans|=(1<<i);
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    code of C/C++(2)
    code of C/C++ (1)
    dll 的编写和使用
    Python基础练习-数据类型与变量part2
    Python基础练习-数据类型与变量
    python基础练习-循环
    Linux grep
    nginx反向代理
    正则表达式
    Linux samba ing
  • 原文地址:https://www.cnblogs.com/five20/p/10015343.html
Copyright © 2020-2023  润新知