bzoj P4033 [HAOI2015]树上染色

15 篇文章 0 订阅

传送门

这道题很妙啊。反正我是想不出来。。

首先有一个很容易想到的状态,设f[i][j]表示子树为i,i涂了j个节点的距离和。嗯这个还是能想到的,然后考虑转移,然后就被虐成zz了。。

分析子树i与儿子所连边的贡献,可以发现这条边的贡献等于他左右两边同色点数乘积和(左黑点数*右黑点数+左白点数*右白点数)。于是我们发现可以进行转移了,就是一个树形dp,f[x][j]=max(f[x][j],f[x][j-l]+f[t[i].v][l]+t[i].w*(l*(k-l)+(size[t[i].v]-l)*(n-size[t[i].v]-k+l)),解释一下,x为当前子树,枚举j(子树的节点数),枚举l(儿子以下的节点数)。

注意:long long炒鸡坑和初始化!!!

代码:

#include<iostream>
#include<stdio.h> 
#include<string.h>
#define ll long long
using namespace std;
const int Maxn=2010;
const ll oo=2e18;
struct node
{
	int v;
	int w;
	int next;
}t[Maxn<<1];
ll f[Maxn][Maxn];
int head[Maxn],size[Maxn];
int n,k,cnt=0;
inline void Insert(int u,int v,int w)
{
	cnt++;
	t[cnt].v=v;
	t[cnt].w=w;
	t[cnt].next=head[u];
	head[u]=cnt;
}
inline void dfs(int x,int fa)
{
	size[x]=1;
	f[x][0]=f[x][1]=0;
	for(int i=head[x];i;i=t[i].next)
	{
		if(t[i].v==fa)
			continue;
		dfs(t[i].v,x);
		size[x]+=size[t[i].v];
	}
	for(int i=head[x];i;i=t[i].next)
	{
        if(t[i].v==fa)
			continue;
        for(int j=min(size[x],k);j>=0;j--)
	        for(int l=0;l<=min(size[t[i].v],j);l++)
	        	{
	        		ll p=(((ll)t[i].w)*((ll)l)*((ll)(k-l)))+((ll)t[i].w)*((ll)(size[t[i].v]-l))*((ll)(n-size[t[i].v]-k+l));
					f[x][j]=max(f[x][j],f[x][j-l]+f[t[i].v][l]+p);
				}
	}
}
int main()
{
	int a,b,c;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		Insert(a,b,c);
		Insert(b,a,c);
	}
	for(int i=0;i<=n;i++)
		for(int j=0;j<=n;j++)
			f[i][j]=-oo;
	dfs(1,0);
	printf("%lld",f[1][k]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值