[HAOI2015]树上染色

嘟嘟嘟


首先这一眼看出来,要树形dp。
然后发现状态不好设,刚开始我想的是dp[i][j]表示以(i)为根的子树,选了(j)个黑点的最大价值。结果就不会转移了。
转移的时候想考虑(<u, v, w>)这一条边的贡献,但是发现这个状态的转移所涉及的不只是这一条边,还有子树中的边,于是就彻底gg了。
还是看了题解。
题解也是考虑贡献,而且也是考虑每一条边,但最大的区别是,我们枚举边,然后考虑边两侧的点对答案的贡献。
令dp[i][j]表示以(i)为根的子树,选了(j)个黑点,已经枚举到第(x)条边时,当前答案的最大值。
因为dfs的时候就相当于枚举边了,所以这一维自然省去。
于是对于黑点,有(j * (K - j) * w(u, v))的贡献,
对于白点,有((K - j) * (n - size[v] - (K - j)) * w(u, v))的贡献。
最后记得要初始化dp数组为-INF,因为有些状态不合法。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 2e3 + 5;
inline ll read()
{
	ll ans = 0;
	char ch = getchar(), last = ' ';
	while(!isdigit(ch)) {last = ch; ch = getchar();}
	while(isdigit(ch)) {ans = (ans << 1) + (ans << 3) + ch - '0'; ch = getchar();}
	if(last == '-') ans = -ans;
	return ans;
}
inline void write(ll x)
{
	if(x < 0) x = -x, putchar('-');
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}

int n, K;
struct Edge
{
	int nxt, to; ll w;
}e[maxn << 1];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y, ll w)
{
	e[++ecnt] = (Edge){head[x], y, w};
	head[x] = ecnt;
}

int siz[maxn];
ll dp[maxn][maxn];
In void dfs(int now, int _f)
{
	siz[now] = 1; dp[now][0] = dp[now][1] = 0;
	for(int i = head[now], v; i != -1; i = e[i].nxt)
	{
		if((v = e[i].to) == _f) continue;
		dfs(v, now);
		siz[now] += siz[v];
		for(int j = min(siz[now], K); j >= 0; --j)
			for(int k = 0; k <= min(j, siz[v]); ++k)
				dp[now][j] = max(dp[now][j], dp[now][j - k] + dp[v][k] + 1LL * k * (K - k) * e[i].w + 1LL * (siz[v] - k) * (n - siz[v] - K + k) * e[i].w);
	}
}

int main()
{
	Mem(head, -1);
	n = read(); K = read();
	for(int i = 1; i < n; ++i)
	{
		int x = read(), y = read(); ll w = read();
		addEdge(x, y, w); addEdge(y, x, w);
	}
	for(int i = 1; i <= n; ++i) fill(dp[i] + 1, dp[i] + K + 1, INF);
	//带劲的操作 
	dfs(1, 0);
	write(dp[1][K]), enter;
	return 0;
}
原文地址:https://www.cnblogs.com/mrclr/p/10209710.html