题意:
给出一棵有n(<=1e5)的点的树,现在要计算每条路径的权值的和,权值可以表示为一条路径上每个节点的权值之积,答案对10086取模。
题解:
很显然需要动态规划,定义状态是一件很麻烦的事情QAQ
首先考虑的是根据答案来定义,dp[u]表示的是以u为根的子树的答案,现在考虑往上转移,我们发现这个答案是由两个部分组成的,第一个部分是直链,第二部分是交叉链,如果两个部分混在一起是不好转移的,然后又会发现交叉链的情况与转移无关,那么现在会很自然的定义出状态dp[u]表示以u为根的子树直链的的值,交叉链的值就直接加在答案里面。
现在再考虑转移 :
直链的情况 : dp[u] = ∑dp[v] * w[u]
交叉链的情况 : ans += sum * dp[v],sum = sum + dp[v]
代码:
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 7; const int mod = 10086; #define LL long long vector <int> e[N]; int w[N], n; LL ans, dp[N]; void Dfs (int u, int pre) { dp[u] = w[u]; LL sum = 0; for (int i = 0; i < e[u].size(); ++i) { int v = e[u][i]; if (v == pre) continue; Dfs (v, u); ans = (ans + sum * dp[v]) % mod; sum = (sum + dp[v] * w[u]) % mod; dp[u] = (dp[u] + dp[v] * w[u]) % mod; } ans = (ans + dp[u]) % mod; } int main () { scanf ("%d", &n); for (int i = 1; i <= n; ++i) scanf ("%d", &w[i]); for (int i = 1; i < n; ++i) { int u, v; scanf ("%d%d", &u, &v); e[u].push_back(v); e[v].push_back(u); } Dfs (1, 0); cout << ans << endl; return 0; }
总结:
要想清楚转移,答案的转移不能有冗余,也就是与转移无关的需要剔除,认真发现出一些性质QAQ