[TJOI 2017]异或和

Description

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

Input

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

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

Output

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

Sample Input

3

1 2 3

Sample Output

0

Hint

【样例解释】

序列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

题解

难得一道省选题看一眼就有思路的。一般这种异或都是按位一位一位做的。

定义$sum$为前缀和,我们开两个权值树状数组,一个表示处理到第$i$位时,$sum[j]$的第$i$位为$1$,$0$~$j-1$中$sum$的$1$~$i-1$位的值域,另一个表示$sum[j]$的第$i$位为$0$的情况。

统计答案时,有两种情况:

1.$sum[j]$的第$i$位为$1$,由于$sum$数组是单调递增的(这是一个很重要的性质),那么以$j$为子序列右端点的子序列第$i$位为$1$的情况只有两种:

  (1)左端点第$i$位为$0$,并且$1$~$i-1$位小于右端点($j$)的$1$~$i-1$位;

  (2)左端点第$i$位为$1$,并且$1$~$i-1$位大于右端点($j$)的$1$~$i-1$位。(这里特别说明一下,因为刚才说过了$sum$单调递增,所以能够保证减出来不会出现负数)

2.$sum[j]$的第$i$位为$0$,那么以$j$为子序列右端点的子序列第$i$位为$1$的情况也只有两种:

  (1)左端点第$i$位为$1$,并且$1$~$i-1$位小于右端点($j$)的$1$~$i-1$位;

  (2)左端点第$i$位为$0$,并且$1$~$i-1$位大于右端点($j$)的$1$~$i-1$位。

其实就是竖式减法。

//It is made by Awson on 2017.9.28
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define LL long long
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define sqr(x) ((x)*(x))
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N = 1e5;
const int bit_size = 1e6;
int st[25];
void read(int &x) {
    char ch; bool flag = 0;
    for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
    for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
    x *= 1-2*flag;
}

int sum[N+5], a[N+5];
int c[bit_size+5][2];
int n, maxn;

void add(bool bit, int x) {
    for (; x <= maxn+1; x += lowbit(x)) c[x][bit]++;    
}
int cunt(bool bit, int x) {
    int cnt = 0;
    for (; x; x -= lowbit(x)) cnt += c[x][bit];
    return cnt;
}
void work() {
    st[0] = 1;
    for (int i = 1; i <= 20; i++) st[i] = st[i-1]<<1;
    read(n);
    for (int i = 1; i <= n; i++)
        read(sum[i]), sum[i] += sum[i-1];
    maxn = sum[n];
    int ans = 0;
    for (int i = 0; i <= 20; i++) {
        if (st[i] > maxn) break;
        memset(c, 0, sizeof(c));
        add(0, 1);
        int flag = 0;
        for (int j = 1; j <= n; j++) {
            int tmp = st[i]&sum[j], cnt;
            if (tmp) cnt = cunt(0, a[j]+1)+cunt(1, maxn+1)-cunt(1, a[j]+1);
            else cnt = cunt(1, a[j]+1)+cunt(0, maxn+1)-cunt(0, a[j]+1);
            if (cnt%2) flag ^= 1;
            add((bool)(tmp), a[j]+1);
            a[j] |= tmp;
        }
        ans |= st[i]*flag;
    }
    printf("%d\n", ans);
}
int main() {
    work();
    return 0;
}

 

posted @ 2017-09-29 00:13  NaVi_Awson  阅读(498)  评论(0编辑  收藏  举报