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;
}
全部评论

相关推荐

6 收藏 评论
分享
牛客网
牛客企业服务