acm.hdu.edu.cn/showproblem.php?pid=6060
【题意】
- 给定一棵以1为根的树,把这颗树除1以外的结点划分为k个集合(可以有空集),把1加入划分后的集合
- 每个集合的结点形成一棵最小生成树
- 所有最小生成树的权值之和最大化
【思路】
- 最小生成树,每个点u到root 1都要有唯一的一条路径,那么显然,u到1沿路的每条边贡献都为1
- 现在考虑每条边的贡献
- 对于某条边uv,v是离根更远的结点,以v为根的子树大小为sz[v],显然我们可以通过把sz[v]个结点划分到sz[v]个不同的集合中,使得uv的贡献为sz[v]
- 当然,sz[v]大于k时我们只能取k
【Accepted】
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #include<stack> 9 #include<map> 10 #include<vector> 11 using namespace std; 12 typedef long long ll; 13 int n,k; 14 const int maxn=2e6+3; 15 struct node 16 { 17 int v; 18 ll c; 19 node(int _v,ll _c):v(_v),c(_c){} 20 }; 21 vector<node> vec[maxn]; 22 ll ans; 23 int sz[maxn]; 24 int DFS(int u,int pa) 25 { 26 sz[u]=1; 27 for(int i=0;i<vec[u].size();i++) 28 { 29 int v=vec[u][i].v; 30 if(v==pa) continue; 31 sz[u]+=DFS(v,u); 32 ll cnt=min(sz[v],k); 33 ans+=vec[u][i].c*cnt; 34 } 35 return sz[u]; 36 } 37 int main() 38 { 39 while(scanf("%d%d",&n,&k)!=EOF) 40 { 41 for(int i=0;i<maxn;i++) 42 { 43 vec[i].clear(); 44 } 45 memset(sz,0,sizeof(sz)); 46 for(int i=0;i<n-1;i++) 47 { 48 int u,v; 49 ll c; 50 scanf("%d%d%lld",&u,&v,&c); 51 vec[u].push_back(node(v,c)); 52 vec[v].push_back(node(u,c)); 53 } 54 ans=0ll; 55 DFS(1,-1); 56 printf("%lld\n",ans); 57 } 58 return 0; 59 }