hdu 6060 RXD and dividing
题目大意,一棵树有n个节点,1为根节点,将2~n这n各节点分为k部分。这k个部分没有交集,可以为空,求1到k部分的和最大。
解题思路:这里提到了最小斯坦纳树,对此一无所知不知道要怎么弄,后来发现这里有n个节点n-1条边这样形成的树本来就是一棵最小生成树了,然后最小生成树又是斯坦纳树的一种特殊情况,所以这里可以忽略斯坦纳树的情况。而我们需要考虑的是某些边的重用情况。
记边 (u, v) 的权重为 w[v],以点 v 为根的子树的节点总数为 sz[v],那么答案就是 w[v] * min(sz[v], k) 对每个点求个和。这里我们要明白一点那就是,如果想让一条边尽量多用,那么它的子节点就应该尽可能分成多部分,这里他分成最多的部分是min(sz[i],k)最多是k部分,因为题目要求把n-1个节点分成k部分,所以w【v】最大的贡献是k,开始我一直觉得,这样算不能囊括所有的节点,后来才发现,原来这里分成的k部分是以叶子节点来分的,若k=1 就直接是原来那棵树的权重,若k>=2,先将叶子结点分成一部分,然后可以视为叶子结点已经不存在,再将新的叶子节点看成一部分,剩余的看做一部分,这样就可以保证囊括所有节点了。
code:
#include <bits/stdc++.h>
using namespace std;
struct Node
{
int v,w;
};
vector<Node> e[1000010];
int sz[1000010],w[1000010];
void dfs(int v,int pre)
{
sz[v] = 1;
for(int i=0;i<e[v].size();i++)
{
int u = e[v][i].v;
if(u == pre)
continue;
w[u] = e[v][i].w;
dfs(u,v);
sz[v] += sz[u];
}
}
int main()
{
int n,k;
while(~scanf("%d %d",&n,&k))
{
for(int i=0;i<=n;i++)
e[i].clear();
int a,b,c;Node temp;
for(int i=1;i<n;i++){
scanf("%d %d %d",&a,&b,&c);
temp.v = b; temp.w = c;
e[a].push_back(temp);
temp.v = a; temp.w = c;
e[b].push_back(temp);
}
memset(sz,0,sizeof(sz));
memset(w,0,sizeof(w));
dfs(1,0);
long long ans = 0;
for(int i = 2;i<=n;i++)
ans += w[i]*(long long)min(sz[i],k);
printf("%lld\n",ans);
}
return 0;
}