zoj3820 树直径上二分选点

来源:互联网 发布:淘宝已有图片找同款 编辑:程序博客网 时间:2024/05/06 03:17

Building Fire Stations
Time Limit: 5 Seconds Memory Limit: 131072 KB Special Judge
Marjar University is a beautiful and peaceful place. There are N buildings and N - 1 bidirectional roads in the campus. These buildings are connected by roads in such a way that there is exactly one path between any two buildings. By coincidence, the length of each road is 1 unit.

To ensure the campus security, Edward, the headmaster of Marjar University, plans to setup two fire stations in two different buildings so that firefighters are able to arrive at the scene of the fire as soon as possible whenever fires occur. That means the longest distance between a building and its nearest fire station should be as short as possible.

As a clever and diligent student in Marjar University, you are asked to write a program to complete the plan. Please find out two proper buildings to setup the fire stations.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains an integer N (2 <= N <= 200000).

For the next N - 1 lines, each line contains two integers Xi and Yi. That means there is a road connecting building Xi and building Yi (indexes are 1-based).

Output

For each test case, output three integers. The first one is the minimal longest distance between a building and its nearest fire station. The next two integers are the indexes of the two buildings selected to build the fire stations.

If there are multiple solutions, any one will be acceptable.

Sample Input

2
4
1 2
1 3
1 4
5
1 2
2 3
3 4
4 5
Sample Output

1 1 2
1 2 4
题意:有n个建筑,n-1条边将n个建筑连接起来,每条边成都为1,现在要选择两个建筑点设立消防站,为了使距离消防站最远的建筑 距离消防站的距离最小,该怎样选择那两个站点设立消防站,输出这个最小的最大距离以及这两个站点。
如果是一条链的话很明显,将链三等分的两个点便是了。这里写图片描述
如果是一颗树的话,道理也是一样的,肯定在树的直径上,但是不一定是数的直径的三等分点,但可以肯定的是这两个点是关于直径中点对称的。
我们只需要找一个点就可以了,另外一个点关于重点对称,而找树的直径很简单,随意从一个s1点出发,找到距离这个点最远的点s2,再从s2出发找到距离s2最远的点s3,那么s2到s3的路径便是树的直径。
把树的直径用数组保存下来,在这个直径上进行二分,设k1为第一个点的下标,那么第二点的下标 k2=m(直径上的点总数)-k1-1,当满足从road[k1]**或则**road[k2]到其余所有到点小于k1时(k1刚好就是从第0个点到k1点的距离),k1左移(二分意义上的左移),否则k1右移,二分的复杂度是logn,而遍历查询每个点到road[k1],road[k2]距离是n,所以时间是可以接受的。如果k1==k2,随便给k2附一个值,因为如果取中点,另外一个点取哪个都可以,不会影响中间的选择。
代码:

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <algorithm>#include <string.h>#include <queue>using namespace std;const int Maxn=200000+100;const int inf=0x3f3f3f3f;struct Edge{    int to,cap,next;} edge[Maxn*2];int head[Maxn],tot,N;void init(){    tot=0;    memset(head,-1,sizeof(head));}void addedge(int u,int v,int w){    edge[tot].to=v;    edge[tot].cap=w;    edge[tot].next=head[u];    head[u]=tot++;}int dis[Maxn],vis[Maxn],pre[Maxn];int road[Maxn],sum;int  bfs(int s){    int v,u,max_d=0,res;    queue<int>que;    que.push(s);    memset(vis,0,sizeof(vis));    vis[s]=1;    while(!que.empty())    {        u=que.front();        que.pop();        for(int i=head[u]; i!=-1; i=edge[i].next)        {            v=edge[i].to;            if(!vis[v])            {                dis[v]=dis[u]+1;                if(dis[v]>max_d)                {                    max_d=dis[v];                    res=v;                }                pre[v]=u;                que.push(v);                vis[v]=1;            }        }    }    return res;}int flag[Maxn];bool  check(int mid){    memset(dis,0,sizeof(dis));    memset(flag,0,sizeof(flag));    bfs(road[mid]);    for(int i=1; i<=N; i++)    {        if(dis[i]<=mid)            flag[i]=1;    }    memset(dis,0,sizeof(dis));    bfs(road[sum-mid-1]);    for(int i=1; i<=N; i++)    {        if(dis[i]<=mid)            flag[i]=1;    }    for(int i=1; i<=N; i++)    {        if(flag[i]==0)            return false;    }    return true;}int main(){    int T,n,u,v,temp,s1,s2;    cin>>T;    while(T--)    {        scanf("%d",&n);        N=n;        init();        for(int i=1; i<n; i++)        {            scanf("%d%d",&u,&v);            addedge(u,v,1);            addedge(v,u,1);        }        memset(dis,0,sizeof(dis));        s1=bfs(1);        memset(dis,0,sizeof(dis));        memset(pre,-1,sizeof(pre));        s2=bfs(s1);        u=s2;        sum=0;        while(u!=-1)        {            road[sum++]=u;            u=pre[u];        }        int l,r,mid,ans[3];        l=0;        r=sum;        while(l<=r)        {            mid=(r+l)/2;            if(check(mid))            {                ans[0]=mid;                ans[1]=road[mid];                ans[2]=road[sum-mid-1];                if(ans[1]==ans[2])///如果两个点是同一个点,随便把一个点换掉就行                {                    for(int i=0; i<sum; i++)                        if(ans[1]!=road[i])                        {                            ans[2]=road[i];                            break;                        }                }                r=mid-1;            }            else                l=mid+1;        }        printf("%d %d %d\n",ans[0],ans[1],ans[2]);    }    return 0;}/**15101 22 33 44 55 66 74 88 99 10*/
原创粉丝点击