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 子孙个数 

你可能感兴趣的:(搜索,想法题)