【Luogu】P3760异或和(权值树状数组)

  题目链接

  再次声明以后我见到位运算一定第一时间想把它拆成每一位算

  本题就是有个前缀和sum[],然后让你求每一位有多少对i,j满足sum[i]-sum[j]在那一位上是1

  考虑怎样才能减出1来

  如果sum[i]在这一位是1的话,那么就需要j是0且sum[i]前面的数小于sum[j]前面的数,这样不至于一减减退位了,把sum[i]这一位的1减没了

  如果是0同理

  考虑用权值树状数组维护。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<cstdlib>
#define maxn 1000010
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

inline int low(int x){    return x&(-x);    }

int tot;
int sum[maxn];

struct Tree{
    int tree[maxn];
    void clear(){    memset(tree,0,sizeof(tree));    }
    inline void add(int pos){
        while(pos<=tot){
            tree[pos]++;
            pos+=low(pos);
        }
    }
    inline int ask(int pos){
        int ans=0;
        while(pos>0){
            ans+=tree[pos];
            pos-=low(pos);
        }
        return ans;
    }
}zero,one;

int main(){
    int 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;
        zero.add(1);
        for(int j=1;j<=n;++j){
            int now=(sum[j]%(1<<i))+1;
            if(((sum[j]>>i)&1)==1){
                cnt+=zero.ask(now);
                cnt+=one.ask(tot)-one.ask(now);
                one.add(now);
            }
            else{
                cnt+=zero.ask(tot)-zero.ask(now);
                cnt+=one.ask(now);
                zero.add(now);
            }
        }
        //printf("%d %d\n",i,cnt);
        if(cnt&1)    ans+=1<<i;
    }
    printf("%d",ans);
    return 0;
}

 

posted @ 2018-05-04 10:40 Konoset 阅读(...) 评论(...) 编辑 收藏