码迷,mamicode.com
首页 > 其他好文 > 详细

P3760 [TJOI2017]异或和

时间:2018-11-25 14:38:51      阅读:170      评论:0      收藏:0      [点我收藏+]

标签:中学   前缀和   nbsp   要求   游记   如何快速   问题   博客   结束   

题目描述

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

输入输出格式

输入格式:

 

第一行输入一个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;
}

 

P3760 [TJOI2017]异或和

标签:中学   前缀和   nbsp   要求   游记   如何快速   问题   博客   结束   

原文地址:https://www.cnblogs.com/five20/p/10015343.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!