标签:并集 continue stdin lin eof define flag std 现在
我们用贡献法考虑一条边会被算多少次
我们可能会设为\(f_{i,j,k}\)表示以\(i\)为根,前\(j\)个儿子,选\(k\)个黑点,对现在前j个子树所在集合的最大贡献
但这样我们合并集合的时候,我们漏计算了原小集合的边到大集合的贡献,
因此设为以\(i\)为根子树,前\(j\)个儿子,选\(k\)个黑点对整棵树的最大贡献
这样一条边会在第一次用他合并时就计算时就算出它对最终答案贡献
注意一下\(size\)合并位置
#include<bits/stdc++.h>
#define re register
#define INF 0x3f3f3f3f3f3f3f3f
#define N 2005
using namespace std;
template<typename _ll>
inline void read(re _ll& x){
re char opt;re _ll flag=1,res=0;
while((opt=getchar())<'0'||opt>'9')if(opt=='-')flag=-1;
while(opt>='0'&&opt<='9'){res=(res<<3)+(res<<1)+opt-'0';opt=getchar();}
x=res*flag;
}
typedef long long ll;
struct Edge{
ll to,next,v;
}e[N<<1];
ll dp[N][N],n,K,cnt,h[N],size[N];
inline void AddEdge(re ll x,re ll y,re ll z){e[++cnt]=(Edge){y,h[x],z};h[x]=cnt;}
inline void tree_pack(re ll x,re ll prt){
re ll i,j,k,y;size[x]=1;
dp[x][1]=*dp[x]=0;
for(i=h[x];i;i=e[i].next){
y=e[i].to;if(y==prt)continue;
tree_pack(y,x);
size[x]+=size[y];
for(j=min(size[x],K);j>=0;--j)
for(k=0;k<=min(j,size[y]);++k)
dp[x][j]=max(dp[x][j],dp[y][k]+dp[x][j-k]+e[i].v*(k*(K-k)+(size[y]-k)*(n-size[y]-K+k)));
}
}
inline void Read(void){
re ll i,x,y,z;
read(n);read(K);
for(i=1;i<n;++i){read(x);read(y);read(z);AddEdge(x,y,z);AddEdge(y,x,z);}
}
int main(void){
// freopen("income.in","r",stdin);
// freopen("income.out","w",stdout);
memset(dp,0xc0,sizeof dp);
Read();
tree_pack(1,0);
printf("%lld\n",dp[1][K]);
return 0;
}
/*
3 1
1 2 1
1 3 2
*/
标签:并集 continue stdin lin eof define flag std 现在
原文地址:https://www.cnblogs.com/66t6/p/11622918.html