统计损失

题意:

给出一棵有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

posted @ 2016-10-28 08:49  xgtao  阅读(149)  评论(0编辑  收藏  举报