bzoj 4033: [HAOI2015]树上染色【树形dp】

准确的说应该叫树上分组背包?并不知道我写的这个叫啥
设计状态f[u][j]为在以点u为根的子树中有j个黑点,转移的时候另开一个数组,不能在原数组更新(因为会用到没更新时候的状态),方程式为g[j+k]=max(g[j+k],f[u][j]+f[e[i].to][k]+(k*(m-k)+(si[e[i].to]-k)*(n-m-(si[e[i].to]-k)))*e[i].va);,其中m'为题目描述中的k。
关于这个方程的由来,考虑一条边对答案的贡献,显然是这条边一边的黑点数量*另一边的黑点数量+一边的白点数量*另一边的白点数量,再乘上边权。
注意:size要在dp中加,dp完一棵子树再加上这棵子树的size。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=2005;
long long n,m,h[N],cnt,f[N][N],si[N],g[N];
struct qwe
{
	long long ne,to,va;
}e[N<<1];
long long read()
{
	long long r=0,f=1;
	char p=getchar();
	while(p>'9'||p<'0')
	{
		if(p=='-')
			f=-1;
		p=getchar();
	}
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r*f;
}
void add(long long u,long long v,long long w)
{
	cnt++;
	e[cnt].ne=h[u];
	e[cnt].to=v;
	e[cnt].va=w;
	h[u]=cnt;
}
// void dfs(long long u,long long fa)
// {
	// si[u]=1;
	// for(long long i=h[u];i;i=e[i].ne)
		// if(e[i].to!=fa)
		// {
			// dfs(e[i].to,u);
			// si[u]+=si[e[i].to];
		// }
// }
void dp(long long u,long long fa)
{
	si[u]=1;
	for(long long i=h[u];i;i=e[i].ne)
		if(e[i].to!=fa)
		{
			dp(e[i].to,u);
			memset(g,0,sizeof(g));
			for(long long j=0;j<=min(m,si[u]);j++)
				for(long long k=0;k<=min(m,si[e[i].to]);k++)
					if(j+k<=m)
						g[j+k]=max(g[j+k],f[u][j]+f[e[i].to][k]+(k*(m-k)+(si[e[i].to]-k)*(n-m-(si[e[i].to]-k)))*e[i].va);
			for(long long j=0;j<=m;j++)
				f[u][j]=g[j];
			si[u]+=si[e[i].to];
		}
}	
int main()
{
	n=read(),m=read();
	for(long long i=1;i<n;i++)
	{
		long long x=read(),y=read(),z=read();
		add(x,y,z);
		add(y,x,z);
	}
	//dfs(1,0);
	dp(1,0);
	printf("%lld\n",f[1][m]);
	return 0;
}
posted @ 2018-01-29 16:02  lokiii  阅读(144)  评论(0编辑  收藏  举报