Score of a Tree 题解
题意
- 给出一棵有 \(n\) 个节点的有根树,每个节点的初始值为 0 或 1。
- 每一个时刻非叶节点的值都会变为其所有子节点上一个时刻的值异或后的值,而叶节点除 0 时刻外,它的值都为 0。
- 输出当所有节点值都为 0 前每一个时刻中的节点值的总和并对 \(10^9 + 7\) 取模的值。
分析
- 首先我们考虑暴搜,枚举每一个节点可能的值并模拟每一个时刻的值的变化。但是数据范围为 \(2 \times 10^5\) 暴搜会 TLE,需要优化。
- 接着我们分析树不同时刻的变化(
可以手写模拟一下),然后我们就会惊讶的发现除叶节点外,每一个节点第 $i (i > 0) $ 时刻的值都为其第 \(i\) 代子节点的异或值。我们利用这个结论进行记忆化搜索就可以省去模拟每一个时刻的时间。但我们发现时间复杂度任为 \(O(n\times(2^n))\)。还需进一步优化。
- 我们对异或运算进行分析,发现当一个节点的值恒为 0 之前,它的值每一个时刻都有一半的概率为 1。那它的值什么时候恒为 1 呢?显然,如果我们将叶节点设为第 1 层,那么答案是这个节点所在的层数,我们用 \(tmp\) 表示。那么我们就可以得出每一个节点的贡献为 $2^{n-1}\times tmp $。那么我们就将问题转化为了求出每一个节点的层数。其实就是一个裸的树形 dp。
code
#include <bist/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6;
const int mod = 1e9+7;
int n;
vector<int> G[N];
ll pow2[N];
ll dp[N];
ll ans;
void dfs(int u, int k){
int maxx = 0;
for(int v : G[u])
if(v ^ k){
dfs(v, u);
maxx = (maxx > dp[v] ? maxx : dp[v]);
}
dp[u] = maxx + 1;
return ;
}
void init(){
for(int i = 1; i <= n; i++)
G[i].clear();
ans = 0;
return ;
}
void Sol(){
cin >> n;
init();
for(int u, v, i = 1; i < n; i++){
cin >> u >> v;
G[u].emplace_back(v);
G[v].emplace_back(u);
}
dfs(1, 0);
for(int i = 1; i <= n; i++)
ans = (ans + dp[i]) % mod;
cout << pow2[n-1] * ans % mod << "\n";
return ;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
pow2[0] = 1;
for(int i = 1; i <= 2e5; i++)
pow2[i] = (pow2[i-1] << 1) % mod;
int t;
cin >> t;
while(t--){
Sol();
}
return 0ll;
}