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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步