2020 CCPC 长春 F
题意
非常简洁,给你一个有根树。求问 。
分析
我们发现 出现的非常突兀。考虑直接枚举 ,由于每个点对只计算一次。所以只用计算后加入的对前者的贡献。那么一个点的贡献为, 其它子树中权值为 的编号。但是答案为 。这个没有什么结合率和交换率。我们考虑直接拆位。那么令 表示,权值为 ,第 为 的个数。那么一个点的贡献为 。那么现在就是如果统计树上的点对。要么点分治要么树上启发式合并。时间复杂度都是 的。这里不详细展开了。
代码
#include<bits/stdc++.h> using namespace std; #define ll long long int read() { int x = 0,f = 0;char ch = getchar(); while(!isdigit(ch)) {if(ch == '-')f = 1;ch = getchar();} while(isdigit(ch)) {x = x * 10 + ch - '0';ch = getchar();} return f ? -x : x; } const int N = 1e6 + 52100,M = 1e5 + 100; struct Edge {int to,nxt;}e[M << 1]; int f[N][18][2]; int head[M],ecnt; int son[M],si[M],fa[M],L[M],id[M],R[M],Id,n,val[M]; void add(int x,int y) {e[++ecnt] = (Edge){y,head[x]};head[x] = ecnt;} void dfs(int x,int F) { fa[x] = F;id[L[x] = ++Id] = x;si[x] = 1; for(int i = head[x];i;i = e[i].nxt) { int y = e[i].to;if(y == F) continue; dfs(y,x);si[x] += si[y]; if(si[y] > si[son[x]]) son[x] = y; }R[x] = Id; } ll Ans = 0,Lca; void addp(int x) { for(int i = 0;i < 18;i++) Ans += f[val[Lca] ^ val[x]][i][!((x >> i) & 1)] * (1 << i); } void addt(int x) {for(int i = L[x];i <= R[x];i++) addp(id[i]);} void inc(int x,int d) {for(int i = 0;i < 18;i++) f[val[x]][i][(x >> i) & 1]+=d;} void solve(int x,int keep) { for(int i = head[x];i;i = e[i].nxt) { int y = e[i].to; if(y == fa[x] || y == son[x]) continue; solve(y,0); } if(son[x]) solve(son[x],1); Lca = x; for(int i = head[x];i;i = e[i].nxt) { int y = e[i].to; if(y == fa[x] || y == son[x]) continue; addt(y);for(int j = L[y];j <= R[y];j++) inc(id[j],1); } addp(x);inc(x,1); if(keep) return; for(int i = L[x];i <= R[x];i++) inc(id[i],-1); } int main() { n = read(); for(int i = 1;i <= n;i++) val[i] = read(); for(int i = 1,a,b;i < n;i++) { a = read();b = read(); add(a,b);add(b,a); } dfs(1,0);solve(1,0); cout << Ans << endl; return 0; }