• 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) imes 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
    ",f[i][m]);
    	return 0;
    }
    
  • 相关阅读:
    git删除大文件
    正则表达式学习
    python小技巧集锦
    python的编译
    笔记本BIOS按键和启动项选择按键
    Ubuntu 不能识别U盘
    一文读懂Java 11的ZGC为何如此高效
    ELK原理与介绍
    使用uniapp之-在微信小程序内打开腾讯地图app或高德地图app
    使用Git多人协作开发时分支合并流程
  • 原文地址:https://www.cnblogs.com/Ax-Dea/p/12548965.html
Copyright © 2020-2023  润新知