CF1777D题解

  • 分析

    首先看到那个 \(10^{100}\) 再加上样例,我们就能意识到在不是特别多的操作次数后这颗树上的值就会全变成 \(0\)
    因为没有子节点在一次操作后显然就会变成 \(0\),然后第一次叶节点就会变成 \(0\),然后下一次子节点中只有叶节点的就会变成 \(0\),以此类推,理论上最多操作 \(n\) 次一棵树就会全变成 \(0\)
    定义一个点的子树内全为 \(0\) 时为该点彻底变成 \(0\)
    所以问题变成了对于初值的所有情况,求出所有点在彻底变成 \(0\) 前值为 \(1\) 的次数和。

    思考如何计算次数,显然初值共有 \(2^{n}\) 种情况,对于每种情况分别计算是非常低效的。
    所有我们可以对于所有情况一起计算,那么这时我们求的就是每次操作后单点的值为 \(1\) 的情况的频数 \(=\) 频率 \(\times\) 总情况数。
    可以发现对于单点而言,它的初始值为 \(1\) 的频率是 \(\frac{1}{2}\)

    考虑推广到初始后到彻底变成 \(0\) 前的状态,那么加入子节点的异或,假设所有子节点是叶节点,那么对于单个子节点,\(1\) 的频率为 \(\frac{1}{2}\),再加入一个子节点,发现原来 \(0\)\(1\) 的频率互换了,但是由于两者相等,都是 \(\frac{1}{2}\),所以频率保持不变,易发现对于子节点全为叶节点的节点频率也为 \(\frac{1}{2}\),那么一步步往根节点推,再算上初始状态,可以发现所有节点在彻底变成 \(0\) 之前,每次操作后为 \(1\) 的频率都为 \(\frac{1}{2}\)

    那么现在的问题是如何求出彻底变成 \(0\) 所需的次数,注意到彻底变成 \(0\) 前需要一个点的子树内(除了该点)全变成 \(0\),否则因为是从下向上一步步变的,会存在至少一个最高的点,即该点的子节点没有彻底变成 \(0\),那么在下一次该点仍能通过此子节点转移,频率仍为 \(\frac{1}{2}\),所以需要子树全变成 \(0\)
    因为是从下往上一步步变的,所以一个子树全变成 \(0\) 的次数为最长链的长度。算上初状态频率为 \(\frac{1}{2}\) 的次数为最长链长度 \(+1\)

    总情况数为 \(2^{n}\),那么答案为\(\sum_{i=1}^{n}(子树内最长链长度 + 1) \times \frac{1}{2} \times 2^{n} = \sum_{i=1}^{n}(子树内最长链长度 + 1) \times 2^{n - 1}\)

  • 代码

#include <iostream>
#include <vector>
#define int long long
using namespace std;
constexpr int MAXN(1000007);
constexpr int mod(1000000007);
vector <int> e[MAXN];
int cl[MAXN];
int T, n, ans;
inline void read(int &temp) { cin >> temp; }
inline int ksm(int base, int k) {
	int res(1);
	while (k) {
		if (k & 1)  res = res * base % mod;
		base = base * base % mod, k >>= 1;
	}
	return res;
}
void dfs(int u, int fa) {
	int res(0);
	for (auto v : e[u]) {
		if (v == fa)  continue;
		dfs(v, u);
		res = max(res, cl[v]);
	}
	cl[u] = res + 1;
}
inline void work() {
	for (int i(1); i <= n; ++i)  e[i].clear();
	ans = 0;
	read(n);
	for (int i(1), u, v; i < n; ++i)  read(u), read(v), e[u].push_back(v), e[v].push_back(u);
	dfs(1, 0);
	for (int i(1); i <= n; ++i)  ans = (ans + cl[i]) % mod;
	ans = ans * ksm(2, n - 1) % mod;
	cout << ans << endl;
}
signed main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	read(T);
	while (T--)  work(); 
	return 0;
}
posted @ 2023-10-25 09:47  Kazdale  阅读(176)  评论(0编辑  收藏  举报