ZOJ - 3820 B - Building Fire Stations

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liufengwei1/article/details/84489315

从一棵树取出两个点x1,x2,对于一个点x,dist[x]=min(dis[x1],dis[x2]),问这个dist[x]的最大值最小是多少。

对于一个点,他的最远点之一是某一条树的直径的端点(因为求树的直径就是随便找一个点找到任意一个最远点,把他当树的直径的端点之一)

那我们只要取出任意一条树的直径长度为a[0]放到a数组中,a[1]......a[a[0]],然后二分答案mid,那么取得点就是a[1+mid],a[a[0]-mid],因为这两个点到a[1]和a[a[0]]的长度就是mid。。然后只要从这两个点开始跑最短路看其他点是否距离<=mid就行了,显然,mid越大,越有可能使其他点都<=mid,满足二分性质。

#include<bits/stdc++.h>
#define maxl 200010

using namespace std;

int n,cnt,mxlen,mxed;
int ans,ansl,ansr;
int a[maxl],frm[maxl];
int ehead[maxl],dis[maxl];
struct ed
{
  int to,nxt;
}e[maxl<<1];
bool vis[maxl];
queue <int> q;

inline void add(int u,int v)
{
  e[++cnt].to=v;e[cnt].nxt=ehead[u];ehead[u]=cnt;
}

inline void prework()
{
  scanf("%d",&n);
  memset(ehead,0,sizeof(int)*(n+1));
  int u,v;cnt=0;
  for(int i=1;i<=n-1;i++)
    {
      scanf("%d%d",&u,&v);
      add(u,v);add(v,u);
    }
}

inline void dfs(int u,int fa)
{
  int v;
  for(int i=ehead[u];i;i=e[i].nxt)
    {
      v=e[i].to;
      if(v==fa) continue;
      dis[v]=dis[u]+1;frm[v]=u;
      if(dis[v]>mxlen)
		mxlen=dis[v],mxed=v;
      dfs(v,u);
    }
}

inline bool jug(int mid)
{
	for(int i=1;i<=n;i++)
    	vis[i]=false,dis[i]=0;
  	while(!q.empty())
    	q.pop();
    int sum=n-2,u,v;
    if(1+mid!=a[0]-mid)
    {
		q.push(a[1+mid]),q.push(a[a[0]-mid]);
		vis[a[1+mid]]=true,vis[a[a[0]-mid]]=true;
		sum=n-2;
	}
	else
		q.push(a[1+mid]),vis[a[1+mid]]=true,sum=n-1;
	
	while(!q.empty() && dis[q.front()]<=mid)
	{
		u=q.front();q.pop();
		for(int i=ehead[u];i;i=e[i].nxt)
		{
			v=e[i].to;
			if(!vis[v] && dis[u]+1<=mid)
			{
			  	dis[v]=dis[u]+1;vis[v]=true;
				q.push(v);sum--;
			}
		}
	}
	if(sum==0)
		return true;
	else
		return false;
}

inline void mainwork()
{
  for(int i=1;i<=n;i++)
    dis[i]=0;
  mxlen=0;mxed=0;
  dfs(1,0);
  
  int st=mxed,u,v;
  mxlen=0;mxed=0;
  for(int i=1;i<=n;i++)
    dis[i]=0;
  dfs(st,0);
  
  a[0]=0;u=mxed;
  while(u!=st)
    {
      a[++a[0]]=u;
      u=frm[u];
    }
  a[++a[0]]=st;
  
  int l=0,r=a[0]/2,mid;
  	while(l+1<r)
  	{
		mid=(l+r)>>1;
  		if(jug(mid))
  			r=mid;
  		else
  			l=mid;
  	}
	if(jug(l))
		ans=l;
	else
		ans=r;
  if(1+ans==a[0]-ans)
    ansl=a[1+ans],ansr=a[1+ans+1];
  else
    ansl=a[1+ans],ansr=a[a[0]-ans];
}

inline void print()
{
  printf("%d %d %d\n",ans,ansl,ansr);
}

int main()
{
  //freopen("B.in","r",stdin);
  int t;
  scanf("%d",&t);
  for(int i=1;i<=t;i++)
    {
      prework();
      mainwork();
      print();
    }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/84489315