P5628 【AFOI-19】面基 (dp + 容斥)

P5628 【AFOI-19】面基 (dp + 容斥)

题目传送门

题目大意:略

题目分析:

首先我们观察数据范围,我们发现给定的图是一棵树,那么我们可以直接 \(dfs\) 来计算重要度,根据乘法原理可知。对于某条边的重要度为边两侧的节点个数的乘积

接下来我们考虑用 \(dp\) 来进行求解,我们令 \(f_{i,j}\) 为以 \(i\) 为根,与 \(i\) 距离小于 \(j\) 的重要度。那么我们可以很容易的得出转移方程

\(f_{u,k} = \sum_{i \in son} f_{i,k-1} + val\)

那么转移方程是否正确呢?答案是否定的,因为在这个转移方程中,只考虑了孩子,而没有考虑父亲,所以我们还需要将父亲的重要度加上,我们假设当前应该转移 \(f_{u,k}\),那么我们需要加上 \(f_{fa,k-1}\),但是这样的话,以 \(u\) 为根的子树又加了一次,所以我们还需要减掉 \(f_{u,k-2}\)。至此,我们可以得出正确的转移方程:

\(f_{u,k} = \sum_{i \in son} f_{i,k-1} + val + f_{fa,k-1} - f_{u,k-2}\)

代码实现:

View code
#include<bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int M = 3e4 + 7 , N = 2e2 + 7;
struct T{int v , val;};
vector<T> e[M];
int dp[M][N] , size[M] , n , k;
void dfs(int x , int fa) {
	size[x] = 1;
	for (auto &i : e[x]) {
		if(i.v == fa) continue;
		dfs(i.v , x);
		i.val = size[i.v] * (n - size[i.v]);
		size[x] += size[i.v];
	}
}
void dfs_1(int x , int  fa) {
	dp[x][0] = 0;
	for (auto i : e[x]) {
		if(i.v == fa) continue;
		dfs_1(i.v , x);
		for(int j = 1; j <= k + 1; ++ j) 
			dp[x][j] += dp[i.v][j - 1] + i.val;
	}
}
void  dfs_2(int x , int fa , int Val) {
	if(fa != 0) {
		for(int j = k + 1; j > 1; -- j) dp[x][j] += dp[fa][j - 1] - dp[x][j - 2];
		dp[x][1] += Val;
	}	
	for(auto i : e[x]) {
		if(i.v == fa) continue;
		dfs_2(i.v , x , i.val);	
	}
}
signed main () {
	ios::sync_with_stdio(0),cin.tie(0);
	cin >> n >> k;
	for(int i = 1; i < n ; ++ i) {
		int u , v; cin >> u >> v;
		e[u].pb({v , 0}) , e[v].pb({u , 0});
	}
	dfs(1 , 0);	
	dfs_1(1 , 0);
	dfs_2(1 , 0 , 0);
	int ans = 0;
	for(int i = 1; i <= n; ++ i) ans = (ans > dp[i][k + 1] ? ans : dp[i][k + 1]);
	cout << ans;
}

但是这道题为什么直接用 \(dp\) 的呢。

  • [\(1\)]: 这道题给作者的第一感觉应该是一个树上 \(dp\) ,所以作者直接向 \(dp\) 的方向想了。
  • [\(2\)]: 这道题是求出最优(最不优)的,那么会首先考虑到 \(dp\)\(贪心\)
posted @ 2022-09-14 15:13  L3067545513  阅读(46)  评论(0编辑  收藏  举报