minimal Steiner Tree 求贡献值
题意:
给出一个minimal Steiner Tree的树,其实就是一个最小生成树,现在要把2-n的节点分为k部分,每一部部分都是一个点的集合,每隔集合加上节点1都有把所有点链接起来的总cost,求其和,输出最大的和即可。
思路:
我们无法把除了1 的节点的其它节点分成k中情况,因为情况太多。但是边的数量和权值都知道,如果能求出每一条边在分成的集合的出现次数并且把其加起来就是answer。题目中要求最大的哪一种情况,并且要和1节点廉洁在一起,那么把1节点当做根节点,考虑到每一条边其贡献值就是其儿子节点成了k部分的次数,所以取儿子节点的个数和k值得最小值就是一种贪心策略,尽可能的分成不同的部分,然后与此条边的成绩就是贡献。
minimal Steiner Tree在此题的影响并不是很大,因为此图就是一个最小生成树,没有多余的边。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
const int maxn = 2000005;
int n,m;
long long sum;
vector<int>edge[maxn];
map<int,int>mp[maxn];
int dfs(int s,int f)
{
int sonnumber = 1;
int len = edge[s].size();
for(int i = 0;i < len; i++) {
int to = edge[s][i];
if(to != f) {
sonnumber += dfs(to,s);
}
}
if(f != -1)
sum += (long long)mp[f][s]*min(m,sonnumber);
return sonnumber;
}
int main(int argc, char const *argv[])
{
//freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&m) != EOF) {
for(int i = 1;i <= n-1; i++ ) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
edge[a].push_back(b);
edge[b].push_back(a);
mp[a][b] = c;
mp[b][a] = c;
}
sum = 0;
dfs(1,-1);
printf("%I64d\n",sum);
for(int i = 1;i <= n; i++) {
edge[i].clear();
mp[i].clear();
}
}
return 0;
}