题目:http://acm.hdu.edu.cn/showproblem.php?pid=4424
题目大意:给你n个点,n-1条边,每个点有一个最大容量 c,要你找一个中心点,使它到其他 n-1 个点的最大容量的和最大,然后输出这个最大值。
思路:很好的一道题啊!因为对于每条边它有一个容量,一个点到另一个点的最大容量受这条路径里容量最小的那条边的容量的限制,所以这里我们考虑先将边的容量从大到小排序,然后一条一条边加进去,那么经过这条边有关系的点,肯定是以这条边为最小边的。现在考虑加进去一条容量为c的边,它连接了两个集合 A、B,如果选择是用 A 集合的中心点,那么就是 s = s_a+c*num_b,num_b 指的是B集合的点数,(*那里还有可能爆int,注意一下就行),选择 B 集合的也一样,然后比较确定一下就行了,再合并集合。如果加进去的这条边的两个端点已经是一个集合的了,那就直接continue,理由很简单,因为已经有一条更大的路径了。答案可能是超 int 的,用 lld 就行。
很容易想到它是一棵树,然后就会一直往树形DP上去想,然后就被坑死吧。。= =
代码如下:
#pragma comment(linker, "/STACK:10240000000000,10240000000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 222222;
typedef __int64 lld;
struct Edge
{
int s,t,c;
bool operator < (const Edge &tmp) const
{
return c > tmp.c;
}
} edge[MAXN];
int fa[MAXN];
int find_fa(int x)
{
if(x == fa[x]) return x;
return fa[x] = find_fa(fa[x]);
}
int num[MAXN];
lld sum[MAXN];
int main()
{
int n;
while(~scanf("%d",&n))
{
int tot = 0;
for(int i = 1;i < n;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
edge[tot].s = a;
edge[tot].t = b;
edge[tot].c = c;
tot++;
}
sort(edge,edge+tot);
for(int i = 1;i <= n;i++)
{
fa[i] = i;
num[i] = 1;
sum[i] = 0;
}
for(int i = 0;i < tot;i++)
{
int a = edge[i].s;
int b = edge[i].t;
int c = edge[i].c;
int fx = find_fa(a);
int fy = find_fa(b);
if(fx == fy) continue;
lld s1 = sum[fx]+(lld)c*num[fy];
lld s2 = sum[fy]+(lld)c*num[fx];
//printf("%d,%d,%d,%d\n",fx,fy,s1,s2);
if(s1 >= s2)
{
fa[fy] = fx;
num[fx] += num[fy];
sum[fx] = s1;
}
else
{
fa[fx] = fy;
num[fy] += num[fx];
sum[fy] = s2;
}
}
//printf("%d\n",find_fa(1));
lld ans = sum[find_fa(1)];
printf("%I64d\n",ans);
}
return 0;
}