Luogu P3917异或序列

题目链接:https://www.luogu.org/problemnew/show/P3917

思路:把序列转换成一颗n+1个节点n条边边权分别为a1 a2 ....an的树(链),每一个区间相当于树上的一条路径,那么问题就转换为求树上任意两点间路径的异或和,令dis[i]为根节点到i的路径异或和,对于两个点u和v,dis[u]^dis[v]对答案的贡献为\sum 2^{i}\left ( dis[u]_i \oplus dis[v]_i = 1 \right ),i为二进制第i位。

           显然,第i位为0和第i位为1 的两个数异或才会对答案有贡献,另cnt[i]为在第i位为1的数量,那么在第i位异或为1的方案数就是\left ( n + 1- cnt_i \right )*cnt_i,那么答案就是\sum 2^i*\left ( n + 1- cnt_i \right )*cnt_i

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
#define M(a,b) memset(a,b,sizeof(a))
#define pb push_back
const int maxn = 100000+10;
const LL mod = 1000000007;
struct Edge {
    int to,nxt;
    LL val;
}e[maxn<<1];
int head[maxn],tot;
LL dis[maxn],cnt[maxn];
LL p[40];
void addedge(int u,int v,LL val) {
    e[tot].to=v;
    e[tot].val=val;
    e[tot].nxt=head[u];
    head[u]=tot++;
    return ;
}
void dfs(int u) {
    for (int i=head[u];~i;i=e[i].nxt) {
        Edge &tmp=e[i];
        int v=tmp.to;
        dis[v]=dis[u]^tmp.val;
        for (int i=0;i<32;++i) {
            (dis[v]&p[i])&&(++cnt[i]);
        }
        dfs(v);
    }
    return ;
}
int main() {

    p[0]=1;
    for (int i=1;i<32;++i) {
        p[i]=p[i-1]<<1;
    }
    int n;
    scanf("%d",&n);

    tot=0;
    for (int i=1;i<=n+1;++i) {
        head[i]=-1;
        cnt[i]=0;
    }

    for (int i=1;i<=n;++i) {
        LL val;
        scanf("%lld",&val);
        addedge(i,i+1,val);
    }
    dis[1]=0;
    dfs(1);
    LL ans=0;
    ++n;
    for (int i=0;i<32;++i) {
        ans+=p[i]*(n*1LL-cnt[i])*cnt[i];
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38000095/article/details/81566524