Luogu P3047 [USACO12FEB]Nearby Cows G

题面

题意简述:

给一个节点数为 \(n\) 且有 \(n-1\) 条边的连通图,给出每个点的点权 \(c_i\) ,对于每一个点,求出与其距离不大于 \(m\) 条路径的点的权值和

首先它是一棵树 (废话)

这题拿上手,不能正经搜索,考虑DP

设计状态 \(f_{i,j}\) 表示第 \(i\) 个点距离不大于 \(j\) 的点权和

那么 \(f_{i,0}\) 即为点权 \(c_i\) (初始化)

\(k\) 为点 \(i\) 经过一条边所到之处, \(num_i\) 表示 \(k\) 的个数,则转移方程为:

\[f_{i,j} = \sum f_{k,j-1} - (num_i-1) \times f_{i,j-2} \]

说明一下减去的部分,因为 \(\sum f_{k,j-1}\) 肯定会重复算 \(num_i\)\(f_{i,j-2}\)

为什么,因为这个状态的定义就是相互的,我们只需要每次加上后一层的点权的总和,将前面的减去即可

大家可以画张图理解一下,其实就是简单的容斥原理。

特殊的,对于 \(j = 1\) 的情况,有转移方程:

\[f_{i,j} = \sum f_{k,j-1} + f_{i,j-1} \]

代码供出

#include<bits/stdc++.h>
using namespace std;
const int maxn = (int)1e5+7;
struct node {                  // 链式前向星存图
	int nxt,dir;
}Edge[maxn<<1];
int head[maxn],num[maxn],cnt;
inline void Add(int u,int v) {
	Edge[++cnt].dir = v,Edge[cnt].nxt = head[u],head[u] = cnt;
	Edge[++cnt].dir = u,Edge[cnt].nxt = head[v],head[v] = cnt;
	num[u]++,num[v]++;
}
int n,m,f[maxn][23]; 		// f[i][j] 表示第i个节点走j步时的总权值 
int main() {
	scanf("%d%d",&n,&m);
	int u,v;
	for(int i=1;i<n;++i) {
		scanf("%d%d",&u,&v);
		Add(u,v);
	}
	for(int i=1;i<=n;++i) scanf("%d",&f[i][0]);    // 初始化
	for(int i=1;i<=m;++i)                          // 进行DP
		for(int j=1;j<=n;++j) {
			for(int k=head[j];k;k=Edge[k].nxt) 
				f[j][i] += f[Edge[k].dir][i-1];
			if(i>1) f[j][i] -= (num[j]-1) * f[j][i-2];
			else f[j][i] += f[j][i-1];
		}
	for(int i=1;i<=n;++i) printf("%d\n",f[i][m]);
	return 0;
}
posted @ 2020-03-22 22:34  AxDea  阅读(163)  评论(0编辑  收藏  举报