[BZOJ4033][树形DP]HAOI2015:树上染色

版权声明:虽然博主很菜,但是还是请注明出处(我觉得应该没人偷我的博客) https://blog.csdn.net/qq_43346903/article/details/89039965

BZOJ4033

这道题最关键的思想在于,我们以往树形DP的时候,常常设 f [ i ] [ j ] f[i][j] 表示点 i i 的子树的 d p dp 值,而这道题对于一个点的子树,它的贡献是子树内所有黑点到子树的根节点的距离之和乘上其兄弟的子树的黑点个数,白点同理,因为只能染k个,所以必须记录染了几个黑点,这样的状态是 O ( n 3 ) O(n^3)
所以我们需要换一种统计贡献的方法,既然点不好统计,何不统计边呢?
一条边的贡献就是其两端点的子树内的黑点个数之积加上白点个数之积,再乘上边权
那么设 f [ i ] [ j ] f[i][j] 表示点 i i 的子树内染 j j 个黑点的最大收益,转移就统计边的贡献就好了

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=2005;
int n,K;
int siz[N],pt[N],vis[N<<1],head[N<<1],nxt[N<<1],tot=0;
ll dep[N],f[N][N],c[N<<1];
inline void add(int x,int y,ll z){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;}
void dfs(int v){
	pt[v]=siz[v]=1;f[v][0]=f[v][1]=0;
	for(int i=head[v];i;i=nxt[i]){
		int y=vis[i];
		if(pt[y]) continue;
		dep[y]=c[i];dfs(y);
		for(int j=min(siz[v],K);~j;j--)
			for(int k=min(siz[y],K-j);~k;k--)
				f[v][j+k]=max(f[v][j+k],f[v][j]+f[y][k]);
		siz[v]+=siz[y];
	}
	for(int i=0;i<=min(siz[v],K);i++) f[v][i]+=dep[v]*(i*(K-i)+(siz[v]-i)*(n-K-siz[v]+i));
}
int main(){
	n=read(),K=read();
	for(int x,y,z,i=1;i<n;i++) x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z);
	memset(f,128,sizeof(f));
	dfs(1);
	cout<<f[1][K];
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43346903/article/details/89039965