HDU 4118 Holiday's Accommodation 贪心(树的重心)

题意:给你n个点和n-1条边,每个边有边权,每个点是一个城市,每个边是一条路,每个城市只有一个人,每个城市的人都要和另外一个城市的人交换家来住,每一个人都要交换,不会有多个人要和同一个人 交换,问交换的时候,两个城市的人都要走相同的距离,当所有的人家都交换完毕的时候,总路程的最大值。


想法:开始看到这道题,最先想到的是暴力求树的直径,但是时间复杂度是O(n^2)超时,n<=100000,显然是不行的。后来就想不出来了,后来看了学长的资料,发现我们要求得就是每一条边被使用了几次,然后直接就可以算出综合了,后来又在网上查了一下,这就和树的重心由关系了。然后就看了一下,今天太晚了,那题就明天再写。

a---------b我提取一条边,我可以算出b的右边有多少个点,a的左边有多少个点,那么必然e(a,b)被用的次数时MIN(zuo_a,you_a)。这里还有一个要注意的点,就是说,我以一个任意点作为原点进行搜索,所以我不知道是从a往b搜还是反过来,所以要选择MIN_a,MIN_b中小的那一个。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=100000+5;
int n,befno,skt[N];
__int64 num[N];
struct node
{int u,v,next,w;
}e[N*2];
int head[N],cnt,vis[N];
void Init()
{memset(head,-1,sizeof(head));cnt=0;
}
void add(int a,int b,int c)
{e[cnt].u=a;e[cnt].v=b;e[cnt].w=c;e[cnt].next=head[a];head[a]=cnt++;
} 
void Input()
{scanf("%d",&n);for(int i=1;i<n;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);add(a,b,c);add(b,a,c); }
}
int MIN(int x,int y)
{if(x<y) return x;return y;
}
int dfs(int u,int fa)
{int aftno=0;for(int i=head[u];i+1;i=e[i].next){int v=e[i].v;if(fa==v) continue;aftno+=dfs(v,u);}skt[u]=aftno;return aftno+1;
}
void treatment(int ca)
{__int64 sum=0;memset(skt,0,sizeof(skt));int k=dfs(1,-1);for(int i=0;i<cnt;i+=2){int v=e[i].v;int u=e[i].u;if(skt[u]<skt[v]){swap(u,v);}sum+=2*e[i].w*MIN(skt[v]+1,n-skt[v]-1);}printf("Case #%d: %I64d\n",ca,sum);
}
int main()
{int t,ca=1;scanf("%d",&t);while(t--){Init();Input();treatment(ca++);}return 0;
} 
//DFS 方向不确定
//一条边最多用几次
//DFS 子孙个数