一起看很美的日落!

一起看很美的日落!

题目描述

牛可乐有一棵由 n 个结点构成的树,第 i 个节点的权值为 ai

我们定义一个连通块 V 的权值为:

  • 当前连通块中两两结点的权值异或和,即 i,jVaiaj

你需要计算全部连通块的权值之和。由于答案可能很大,请将答案对 (109+7) 取模后输出。

此题中的连通块定义为:对于树上的任意一个点集 S,如果 S 中的任意两点 u,v 之间存在一条路径,且路径上的所有点都在 S 中,则称 S 是一个连通块。

输入描述:

第一行输入一个整数 n(1n105) 代表树上的节点数量。

第二行输入 n 个整数 a1,a2,,an(1ai109) 代表每个节点的权值。

此后 n1 行,第 i 行输入两个整数 ui,vi(1ui,vin,uivi) 代表树上第 i 条边连接节点 uivi

输出描述:

输出一个整数,代表全部连通块的权值和。答案可能很大,请将答案对 (109+7) 取模后输出。

示例1

输入

3
5 2 1
1 2
1 3

输出

50

说明

在这个样例中,一共有 6 个连通块,每一个连通块的权值依次为:

  • {1},记为 V1,权值为 i,jV1(aiaj)=a1a1=0
  • {1,2},记为 V2,权值为 i,jV2(aiaj)=(a1a1)+(a1a2)+(a2a1)+(a2a2)=14
  • {1,3},记为 V3,权值为 i,jV3(aiaj)=(a1a1)+(a1a3)+(a3a1)+(a3a3)=8
  • {1,2,3},记为 V4,权值为 i,jV4(aiaj)=28
  • {2},记为 V5,权值为 i,jV5(aiaj)=a2a2=0
  • {3},记为 V6,权值为 i,jV6(aiaj)=a3a3=0

示例2

输入

4
5 6 3 1
1 2
1 3
2 4

输出

142

 

解题思路

  大概想了下发现很复杂要维护很多东西,以为错了就直接看题解,没想到还真这么做。

  假设以节点 1 作为整颗树的根。题目中的连通块其实就是以任意节点为根的子树,因此我们可以按子树的根把所有的连通块(子树)分成 n 类,然后分别求每一类连通块的结果最后累加即可。由于涉及到位运算,我们可以对节点的权值进行拆位分别处理,这样每个节点的权值只有 01 两种情况,最后将算出的结果乘以相应的 2 的次幂,就是该位对答案的贡献。

  现在有一个集合 s,元素只有 01,数量分别记为 c(s,0)c(s,1)。根据异或的性质,该集合中任意两个元素的异或值的和就是 c(s,0)×c(s,1),记为 f(s)。现在有集合 t 要与 s 进行合并,01 的数量就变成了 c(s,0)+c(t,0)c(s,1)+c(t,1),异或值的和就是

f(st)=(c(s,0)+c(t,0))×(c(s,1)+c(t,1))=c(s,0)×c(s,1)+c(t,0)×c(t,1)+c(s,0)×c(t,1)+c(s,1)×c(t,0)=f(s)+f(t)+c(s,0)×c(t,1)+c(s,1)×c(t,0)

  假设有 g(s) 个集合 si 要与 g(t) 个集合 tj 进行合并,那么所有合并结果的异或值的和就是

ijf(sitj)=ij(c(si,0)+c(tj,0))×(c(si,1)+c(tj,1))=jif(si)+ijf(tj)+ic(si,0)×jc(tj,1)+ic(si,1)×jc(tj,0)=g(t)if(si)+g(s)jf(tj)+ic(si,0)×jc(tj,1)+ic(si,1)×jc(tj,0)

  同理合并后的集合数量就是 g(s)×g(t),所有集合 0 的数量就是 g(t)ic(si,0)+g(s)jc(tj,0),所有集合 1 的数量就是 g(t)ic(si,1)+g(s)jc(tj,1)

  假设我们现在处理的是第 k 位,用 au,k 表示二进制下节点 u 权值第 k 位的值。假设 umu 个子节点,记为 v1,v2,,vmu。那么以 u 为根的子树就可以通过以 vi 为根的所有子树进行任意组合得到,可以用 dp 求出以 u 为根的所有子树的任意两个节点异或值的和。

  定义 f(u,i) 表示以 v1,v2,,vi 为根的子树组合得到的所有以 u 为根的子树的任意两个节点异或值的和,g(u,i) 表示以 v1,v2,,vi 为根的子树组合得到的所有以 u 为根的子树的数量,c(u,0,i)c(u,1,i) 分别表示以 v1,v2,,vi 为根的子树组合得到的所有以 u 为根的子树的权值为 01 的节点数量。根据以 vi 为根的子树进行状态划分,可以把以 vi 为根的子树看成上述若干个集合 tj,把以 v1,v2,,vi1 为根的子树组合得到的所有以 u 为根的子树看成若干个集合 sisitj 两两进行合并。

  初始只有一个节点 u,此时有 f(u,0)=au,kg(u,0)=1c(u,au,k,0)=1c(u,¬au,k,0)=0。参考上面的式子,状态转移方程就是

{f(u,i)=f(u,i1)+g(vi,mvi)f(u,i1)+g(u,i1)f(vi,mvi)+c(u,0,i1)c(vi,1,mvi)+c(u,1,i1)c(vi,0,mvi)c(u,0,i)=c(u,0,i1)+g(vi,mvi)c(u,0,i1)+g(u,i1)c(u,0,i1)c(u,1,i)=c(u,1,i1)+g(vi,mvi)c(u,1,i1)+g(u,i1)c(u,1,i1)g(u,i)=g(u,i1)+g(u,i1)g(vi,mvi)

  在代码实现中省略了所有状态的最后一维。

  那么第 k 位对答案的贡献就是 2ki=1nf(i,mi)。最后要求的答案就是 2k=0292ki=1nf(i,mi),多乘一个 2 是因为上述考虑的是两个节点的组合,而题目要求考虑两个节点的排列。

  AC 代码如下,时间复杂度为 O(nlogA)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 1e5 + 5, M = N * 2, mod = 1e9 + 7;

int a[N];
int h[N], e[M], ne[M], idx;
int f[N], g[N], c[N][2];

void add(int u, int v) {
    e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}

void dfs(int u, int p, int k) {
    f[u] = 0, g[u] = 1, c[u][a[u] >> k & 1] = 1, c[u][~a[u] >> k & 1] = 0;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (v == p) continue;
        dfs(v, u, k);
        f[u] = (f[u] + 1ll * f[u] * g[v] + 1ll * f[v] * g[u] + 1ll * c[u][0] * c[v][1] + 1ll * c[u][1] * c[v][0]) % mod;
        c[u][0] = (c[u][0] + 1ll * c[u][0] * g[v] + 1ll * c[v][0] * g[u]) % mod;
        c[u][1] = (c[u][1] + 1ll * c[u][1] * g[v] + 1ll * c[v][1] * g[u]) % mod;
        g[u] = (g[u] + 1ll * g[u] * g[v]) % mod;
    }
}

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    memset(h, -1, sizeof(h));
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v), add(v, u);
    }
    int ret = 0;
    for (int i = 0; i < 30; i++) {
        dfs(1, 0, i);
        for (int j = 1; j <= n; j++) {
            ret = (ret + f[j] * (1ll << i)) % mod;
        }
    }
    cout << ret * 2 % mod;

    return 0;
}

 

参考资料

  2025牛客寒假算法基础集训营2 出题人题解:https://blog.nowcoder.net/n/906fd00ff386438b9d63013a3760e73a

posted @   onlyblues  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2023-01-25 Rank Transform of a Matrix
Web Analytics
点击右上角即可分享
微信分享提示