码迷,mamicode.com
首页 > 编程语言 > 详细

异或和(权值树状数组)

时间:2018-10-31 19:59:58      阅读:152      评论:0      收藏:0      [点我收藏+]

标签:main   span   oid   void   很多   for   size   www.   竞赛   

异或和(权值树状数组)

题目描述

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

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

输入样例

3
1 2 3

输出样例

0

数据范围

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

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

题解

位运算每一位之间没有关系,所以遇到位运算先想每一位分别做。

这个题,我们只需要考虑当前位有多少\(sum[i][j]\)\(1\)。复杂度\(n^2\)显然过不了。

\(sum[i]\)为前缀和。考虑前面有多少个前缀和,被当前前缀和减后,这一位为\(1\)。因为只有为1才对答案有贡献。最后我们只要考虑1的个数就好啦。

怎样算合法?

考虑一个数\(x\),若\(x\)这一位是\(1\),为了不把这一位减没,\(x\)所减的数\(y\)这一位如果是\(1\),则\(y\)这一位往右的数都应比\(x\)这一位往右的数大,因为当减不了的时候会向高位借位;若\(y\)这一位为\(0\),则\(y\)往右的数都应不大于\(x\)往右的数。

\(x\)该位为\(0\),同理。

最后,若这一位是1的个数是奇数,则答案加上这一位。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
long long read(){
//  puts("ZAY AK IOI");
    long long x=0;int f=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return f?-x:x;
}
inline int low(int x){
    return x&(-x);    
}
int n,tot,sum[1000010];
struct Dier{
    int tree[1000010];
    void clear(){memset(tree,0,sizeof(tree));}
    inline void add(int k){
        while(k<=tot) tree[k]++,k+=low(k);
    }
    inline int find(int k){
        int ans=0;
        while(k>0) ans+=tree[k],k-=low(k);
        return ans;
    }
}zero,one;
int main(){
    n=read();
    for(int i=1;i<=n;++i) sum[i]=sum[i-1]+read();//前缀和
    tot=sum[n]+1;
    int ans=0;
    for(int i=0;(1<<i)<=tot;++i){
        zero.clear(),one.clear();
        int cnt=0;//计算1的个数
        zero.add(1);
        for(int j=1;j<=n;++j){
            int now=(sum[j]%(1<<i))+1;//第i位往右的数
            if(((sum[j]>>i)&1)==1) cnt+=zero.find(now)+one.find(tot)-one.find(now),one.add(now);
            else cnt+=zero.find(tot)-zero.find(now)+one.find(now),zero.add(now);
        }
        if(cnt&1) ans+=1<<i;
    }
    printf("%d",ans);
    return 0;
}

异或和(权值树状数组)

标签:main   span   oid   void   很多   for   size   www.   竞赛   

原文地址:https://www.cnblogs.com/kylinbalck/p/9885001.html

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