Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/fontdata.js

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_if_{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 @   AxDea  阅读(164)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示