P3177 [HAOI2015] 树上染色 题解
P3177 [HAOI2015] 树上染色 题解
本题解选自我的树上 dp 小记。
好题。
一开始理所当然地想设计
正确的设计方式应该是 在
设计好数组就转移就很方便了:
上面的
注意转移的上下界和一些细节。原始的大部分题解都是要把 dp 数组 memset 成
为什么会从错误的状态转移而来呢?我们枚举了
时间复杂度的分析建议看这个帖子,这里就不过多赘述了。简单而不一定严谨地说,任意两个点只会在 lca 处合并一次,所以时间复杂度是
本题的困难主要在需要打破以前只计算子树内答案的思维,直接考虑对答案的贡献,正确设计 dp 数组。
这里给出代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=2010;
int n,m,s[N];
long long dp[N][N];
vector<pair<int,int>>v[N];
void dfs(int x,int fa)
{
s[x]=1;
for(auto i:v[x])
{
if(i.fi==fa)continue;
dfs(i.fi,x);
s[x]+=s[i.fi];
for(int j=max(m,s[x]);j>=0;j--)
for(int k=max(j-s[x]+s[i.fi],0);k<=min(j,s[i.fi]);k++)//控制好上下界
dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[i.fi][k]+1ll*k*(m-k)*i.se+1ll*(s[i.fi]-k)*(n-m-s[i.fi]+k)*i.se);
}
}
int main()
{
cin>>n>>m;
m=min(m,n-m);//染黑或染白都是一样的
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
v[x].push_back({y,z});
v[y].push_back({x,z});
}
dfs(1,0);
cout<<dp[1][m];
return 0;
}