题目链接:https://www.luogu.org/problemnew/show/P3917
思路:把序列转换成一颗n+1个节点n条边边权分别为a1 a2 ....an的树(链),每一个区间相当于树上的一条路径,那么问题就转换为求树上任意两点间路径的异或和,令dis[i]为根节点到i的路径异或和,对于两个点u和v,dis[u]^dis[v]对答案的贡献为,i为二进制第i位。
显然,第i位为0和第i位为1 的两个数异或才会对答案有贡献,另cnt[i]为在第i位为1的数量,那么在第i位异或为1的方案数就是,那么答案就是
。
#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;
}