首页 > 技术文章 > [HAOI2015]树上染色(树上dp)

terribleterrible 2018-10-27 14:46 原文

[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;
}

推荐阅读