【题意】
给定N个点,M条边的树。定义d(u,v)为路径u到v的容量,其值为路径上最小的边权。求一个点作为root,使得其他所有点到该点的容量和最小,即sum(d(root,v))最小。求最小值。
【分析】
比赛时想到树形dp,怎么也表示不出状态。
考虑到d(u,v)的值是路径上最小的边权值,那么整个图中边权最小的边一定是一个关键边。选出边权最小的边,可以把树分成两个子树,答案在左子树或者右子树上。并且左右子树上的边权都大于选出的边权,那么假设答案在左子树,那右子树到答案点的容量和就是右子树的点数*当前这个最小的边权。最终答案再加上左子树的答案。
同理,如果在右子树也如此。进行比较即可。
这样就把问题按照边分治了。
合并的时候比较左右子树分别作为答案的大小进行合并即可。
分治考虑对边权排序,依次合并,维护一些树,使用并查集即可。
【Mark】
从整体考虑,比较有意思的一题。


1 #include<stdio.h> 2 #include<math.h> 3 #include<string.h> 4 #include<algorithm> 5 6 #define LL long long 7 #define maxn 200020 8 9 using namespace std; 10 11 12 int cnt[maxn]; 13 LL sum[maxn]; 14 15 int fat[maxn]; 16 17 struct edge{ 18 int u,v; 19 LL val; 20 }seq[maxn]; 21 bool cmp(edge p, edge q) 22 { 23 return p.val > q.val; 24 } 25 int n; 26 27 int find(int u) 28 { 29 if (fat[u]!=u) 30 fat[u] = find(fat[u]); 31 return fat[u]; 32 } 33 int main() 34 { 35 while (scanf("%d",&n)==1) 36 { 37 for (int i=1;i<=n;i++) 38 { 39 fat[i] = i; 40 cnt[i] = 1; 41 sum[i] = 0; 42 } 43 for (int i=1;i<n;i++) 44 { 45 scanf("%d%d%I64d",&seq[i].u,&seq[i].v,&seq[i].val); 46 } 47 sort(seq+1,seq+n,cmp); 48 49 for (int i=1;i<n;i++) 50 { 51 int u = seq[i].u; 52 int v = seq[i].v; 53 LL val = seq[i].val; 54 int fu = find(u); 55 int fv = find(v); 56 57 LL tmp1 = sum[fu] + val*cnt[fv]; 58 LL tmp2 = sum[fv] + val*cnt[fu]; 59 if (tmp1 > tmp2){ 60 fat[fv] = fu; 61 cnt[fu] += cnt[fv]; 62 sum[fu] = tmp1; 63 }else{ 64 fat[fu] = fv; 65 cnt[fv] += cnt[fu]; 66 sum[fv] = tmp2; 67 } 68 69 } 70 printf("%I64d\n",sum[find(1)]); 71 } 72 return 0; 73 }
探讨了在给定的树形结构中寻找最优根节点的问题,通过树形DP和分治策略,实现对树中边权的优化计算,旨在找到使所有点到该点的容量和最小的点。

689

被折叠的 条评论
为什么被折叠?



