统计损失
题意:
给出一棵有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
浙公网安备 33010602011771号