hihocoder1581、Agent Communication(二分+图论)

#1581 : Agent Communication

时间限制:1000ms

单点时限:1000ms

内存限制:256MB

描述

In Peking University, there are many homeless cats. The students of cat club built n nests and some paths for those cats. A path connects two nests directly. All nests are connected by paths and there is only one route between any two nests.

Li Lei is the chief of the cat club. He just got some donation for the cats. So he decides to build a new path which connects two nests to make cats travel more easily. He hopes that after the new path is built, the maximum distance between two nests becomes as short as possible. The distance between two nests is defined as the number of paths along the route between two nests.

Can you help Li Lei to figure out how to build the new path?

输入

The first line of input contains an integer t(t ≤ 50), the number of test cases. Then t test cases follow.

For each case:

The first line of input contains an integer n(n ≤ 1000), the number of nests.

Among the next n-1 lines, each line contains two integer x and y (1 ≤ x, y ≤ n), meaning that there is a path between the x-th nest and the y-th nest.

输出

For each test case, output one integer, the maximum distance of two nests after the new path is built correctly.

样例提示

case 1: build a path between 1 and 5

case 2: build a path between 3 and 5

样例输入

2
6
1 2
2 3
3 4
4 5
3 6
7
1 3
2 3
3 4
4 5
5 6
5 7

样例输出

3
3

 

一、原题地址

点我传送

 

二、大致题意

给出一棵树,要求在树上建立一个传送门连通两个点,假设传送门建立在(x,y)两点上,则这两个点可以直达,不需要代价。现在询问当我们在树上建立了一个传送门之后,树上任意两点之间的距离是多少,要求距离的最大值尽可能的小。现在询问最小的最大值可以是多少。

 

三、思路

在做这道题之前先了解一下HDU5699。与这道题几乎一模一样。HDU的题目是建立在一条链上的情况,那么在树上做的时候,首先得脑补出传送门一定是开在树的直径上是最优的。那么我们只需要预处理出枝干上的点到达直径上的父点的距离是多少,这样就相当于把所有点拍在了直径上,题目也就变成了和5699一样的情况。然后就可以推出式子。

代码中用 ToMain 来表示枝干上两个点到达枝干的费用。p[ ] 数组是用来记录枝干上点对应的直径上的点是哪个。求这两个数组的方式有很多,这里采取的是多次的DFS。

假设传送站的两个端点为(x , y),对于第i个距离大于mid的点对有不等式:|p[i]- x| + |p[j] - y|+ ToMain <= mid,将这个不等式展开并变形得到两个式子:

p[i] + p[j]+ ToMain-mid   <=   x+y  <=  p[i] + p[j] - ToMain+mid

p[i] - p[j]+ ToMain-mid   <=   x-y  <=   p[i] - p[j]  - ToMain+mid

推出式子以后求一下最值比较来作为二分的条件。

 

四、代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int inf = 0x3f3f3f3f;



int T, n;
vector<int>e[1005];
bool vis[1005], Main[1005];	
int head, tail, pre[1005], id[1005];
int disNtoN[1005][1005], disToPre[1005], p[1005];
int BFS(int st)
{
	int ret;
	memset(vis, false, sizeof(vis));
	vis[st] = true;
	queue<int>q;
	q.push(st);
	while (!q.empty())
	{
		ret = q.front();
		q.pop();
		int siz = e[ret].size();
		for (int i = 0; i < siz; i++)
		{
			int to = e[ret][i];
			if (!vis[to])
			{
				q.push(to);
				vis[to] = true;
			}
		}
	}
	return ret;
}
void read()
{
	for (int i = 1; i <= n; i++)e[i].clear();
	scanf("%d", &n);
	for (int i = 1; i <= n - 1; i++)
	{
		int u, v;
		scanf("%d %d", &u, &v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
}
void DFS(int nx, int fa)
{
	pre[nx] = fa;
	int siz = e[nx].size();
	for (int i = 0; i < siz; i++)
	{
		int to = e[nx][i];
		if (to != fa)
		{
			DFS(to, nx);
		}
	}
}
void dfs(int st, int nx, int pa, int step)
{
	disNtoN[st][nx] = step;
	int Size = e[nx].size();
	for (int i = 0; i < Size; i++)
	{
		int to = e[nx][i];
		if (to != pa)dfs(st, to, nx, step + 1);
	}
}
void dfspa(int fir, int nx, int pa, int step)
{
	disToPre[nx] = step;
	p[nx] = p[fir];
	int Size = e[nx].size();
	for (int i = 0; i < Size; i++)
	{
		int to = e[nx][i];
		if (to != pa && (!Main[to]))dfspa(fir, to, nx, step + 1);
	}
}
void FindMain()
{
	head = BFS(1);		//find head
	tail = BFS(head);	//find tail
	DFS(head, -1);
	memset(Main, false, sizeof(Main));
	int now = tail;
	int cnt = 1;
	while (now != -1)
	{
		Main[now] = true;
		p[now] = cnt++;
		now = pre[now];
	}
}
void FindDis()
{
	for (int i = 1; i <= n; i++)dfs(i, i, -1, 0);
	int now = tail;
	while (now != -1)
	{
		dfspa(now, now, -1, 0);
		now = pre[now];
	}
}
bool check(int mid)
{
	int min1 = inf, max1 = -inf, min2 = inf, max2 = -inf;
	for (int i = 1; i <= n; i++)
	{
		for (int j = i + 1; j <= n; j++)
		{
			int x = i, y = j;
			if (disNtoN[x][y] <= mid)continue;
			int ToMain = disToPre[i] + disToPre[j] + 1;
			if (p[x] > p[y])swap(x, y);
			max1 = max(max1, p[x] + p[y] + ToMain - mid);
			min1 = min(min1, p[x] + p[y] - ToMain + mid);
			max2 = max(max2, p[x] - p[y] + ToMain - mid);
			min2 = min(min2, p[x] - p[y] - ToMain + mid);
		}
	}
	if (max1 <= min1&&max2 <= min2)
	{
		if (max1 == min1&&max2 == min2)
		{
			if ((max1 + max2) & 1)return false;
			return true;
		}
		return true;
	}
	return false;
}
int main()
{
	scanf("%d", &T);
	while (T--)
	{
		read();			//读入操作
		FindMain();		//找到树的主干
		FindDis();
		int l, r, ans, mid;
		l = 0, r = n;
		while (l <= r)
		{
			mid = (l + r) >> 1;
			if (check(mid))
			{
				ans = mid;
				r = mid - 1;
			}
			else l = mid + 1;
		}
		printf("%d\n", ans);
	}
	getchar();
	getchar();
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值