[HAOI2015]树上染色(树上dp)

[HAOI2015]树上染色

这种要算点对之间路径的长度和的题,难以统计每个点的贡献.这个时候一般考虑算每一条边贡献了哪些点对.
知道这个套路以后,那么这题就很好做了.
状态:设(dp[u][i])表示u节点(子树里有i个黑点)的子树的边的贡献的和.
转移:转移就很好想了,知道v内的黑点个数j,知道v内的白点数目(sz[v]-j),知道总共的黑点数目(m),知道总共的白点数目((n-m)),知道边权w,那么转移方程显然就是:

[dp[u][i]=max{dp[v][j]+w*(m-j)*j+w*(sz_v-j)*[n-m-(sz_v-j)]} ]

答案就是(dp[1][k])

#include<bits/stdc++.h>
#define maxn 2005
#define ll long long
using namespace std;
int n,m,sz[maxn];
ll dp[maxn][maxn],t[maxn];
int head[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],cnt;
void add(int u,int v,int ww)
{
	nxt[++cnt]=head[u];head[u]=cnt;
	w[cnt]=ww;to[cnt]=v;
}
void dfs(int u,int ff)
{
	dp[u][0]=dp[u][1]=0;sz[u]=1;
	for(int ii=head[u];ii;ii=nxt[ii])
	{
		int v=to[ii];if(v==ff)continue;
		dfs(v,u);sz[u]+=sz[v];
		for(int i=0;i<=m;i++)t[i]=dp[u][i];
		for(int i=m;i>=0;i--)
		{
			for(int j=0;j<=min(i,sz[v]);j++)
			{
				if(t[i-j]==-1)continue;ll res=0,ww=w[ii];
				res=dp[v][j]+ww*(m-j)*j+ww*(sz[v]-j)*(n-m-sz[v]+j);
				//cout<<u<<" "<<v<<" "<<ww<<" "<<i<<" "<<j<<endl;
				dp[u][i]=max(dp[u][i],res+t[i-j]);
			}
		}
		//cout<<u<<"&"<<v<<" ";
		//for(int i=0;i<=m;i++)cout<<dp[u][i]<<" ";cout<<endl;
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1,u,v,ww;i<n;i++)
		scanf("%d%d%d",&u,&v,&ww),add(u,v,ww),add(v,u,ww);
	memset(dp,-1,sizeof(dp));
	dfs(1,0);cout<<dp[1][m]<<endl;
	//for(int u=1;u<=n;u++,cout<<endl)
	//	for(int i=0;i<=m;i++)
	//		cout<<dp[u][i]<<" ";
	return 0;
}

原文地址:https://www.cnblogs.com/terribleterrible/p/9861158.html