hdu6060
题意:给出一颗n个节点的树,要求将2-n号节点分成k部分,然后再将每一部分加上1号节点联通的最小花费,定义为每一部分的val,为其在原图上的最小斯坦纳树,问总的val最大可能是多少。
把1看成整棵树的根. 问题相当于把2∼n每个点一个[1,k]的标号. 然后根据最小斯坦纳树的定义(x,fa(x) ),(x,fa(x) ) 这条边的贡献是 x 子树内不同标号的个数目dif(i) . 那么显然有dif(i)≤min(k,sz(i)),sz(i) 表示子树大小. 可以通过构造让所dif(i) 都取到最大值. 所以答案就是 w[x] ∗min( sz(x) , k ),时间复杂度O(n),w[x]表示点x与其父节点的连边的权值。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
typedef long long LL;
using namespace std;
const LL MAXN=1e6+10;
struct node
{
LL next,v;
LL w;
} edge[MAXN*2];
LL head[MAXN],tot;
LL son[MAXN];
LL dis2[MAXN];
LL n,k;
void add(LL u,LL v,LL w)
{
edge[tot].v=v;
edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot++;
}
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void dfs(LL u,LL pre)
{
son[u]=1;
LL v;
for(LL i=head[u]; i!=-1; i=edge[i].next)
{
v=edge[i].v;
if(v==pre)
continue;
dis2[v]=edge[i].w;
dfs(v,u);
son[u]+=son[v];
}
}
int main()
{
LL u,v,w;
while(scanf("%lld%lld",&n,&k)!=-1)
{
init();
for(LL i=1; i<n; i++)
{
scanf("%lld%lld%lld",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs(1,-1);
LL sum=0;
for(LL i=2; i<=n; i++)
sum+=(LL)(dis2[i]*min(son[i],k));
printf("%lld\n",sum);
}
return 0;
}
/**
5 2
1 2 3
2 3 4
2 4 5
2 5 6
6 1
1 2 1
1 3 2
1 4 3
2 5 5
2 6 6
5 2
1 2 3
2 3 4
3 4 5
3 5 6
*/