【NOI2013】快餐店【基环树】【树的直径】【set】

传送门

题意:给一棵 n n n个点的基环树,找一个点(可以在边上),求所有节点到这个点的最大值的最小值。

n ≤ 1 e 5 n \leq1e5 n1e5

先考虑一棵普通树的情况

显然是直径长度的一半

因为如果有个点大于直径长度的一半,显然可以找一个更长的链,所以这个长度可以覆盖所有点。而如果小于,不能覆盖直径的端点。

如果是基环树,发现仍然可以按普通树的形式覆盖所有点。也就是断掉环上的一条边后的最小直径。

非环边是不受影响的,分别跑一次直径记录下来。

对每个环上的点记录最大深度,套路性地断环为链,set瞎搞一下就出来了

和前面的最大直径取max输出

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <utility>
#include <set>
#define MAXN 100005
#define MAXM 200005
using namespace std;
typedef long long ll;
inline int read()
{
     int ans=0;
     char c=getchar();
     while (!isdigit(c)) c=getchar();
     while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
     return ans;
}
struct edge{int u,v,w;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt;
void addnode(int u,int v,int w)
{
  e[++cnt]=(edge){u,v,w};
  nxt[cnt]=head[u];
  head[u]=cnt;
}
bool vis[MAXN],isrt[MAXN];
int lop[MAXN],x[MAXN],tot,tmp;
ll dep[MAXM];
void dfs(int u,int f)
{
     vis[u]=true;
     for (int i=head[u];i;i=nxt[i])
	  if (!vis[e[i].v]&&e[i].v!=lop[1])
	  {
	       dfs(e[i].v,u);
	       if (isrt[e[i].v]&&e[i].v!=tmp) isrt[lop[++tot]=u]=true,x[tot]=e[i].w;
	  }
	  else
	       if (e[i].v!=f&&e[i].v!=lop[1])
		    isrt[lop[++tot]=u]=true,x[tot]=e[i].w,tmp=e[i].v;
     vis[u]=false;
}
ll dis[MAXN]={-1};
int mx;
void getdis(int u,int f)
{
     if (dis[u]>dis[mx]) mx=u;
     for (int i=head[u];i;i=nxt[i])
	  if (e[i].v!=f&&!isrt[e[i].v])
	  {
	       dis[e[i].v]=dis[u]+e[i].w;
	       getdis(e[i].v,u);
	  }
}
typedef pair<ll,int> pi;
multiset<pi> s1,s2;//+,-
inline ll calc(){return s1.rbegin()->second==s2.rbegin()->second? max((++s1.rbegin())->first+s2.rbegin()->first,s1.rbegin()->first+(++s2.rbegin())->first):s1.rbegin()->first+s2.rbegin()->first;}
ll sum[MAXM];
int main()
{
     int n=read();
     for (int i=1;i<=n;i++)
     {
	  int u,v,w;
	  u=read(),v=read(),w=read();
	  addnode(u,v,w);addnode(v,u,w);
     }
     dfs(1,0);
     ll ans0=0;
     for (int i=1;i<=tot;i++)
     {
	  isrt[lop[i]]=false;
	  mx=0;
	  dis[lop[i]]=mx=0;
	  getdis(lop[i],0);
	  dep[i]=dis[mx];
	  dis[mx]=0;
	  getdis(mx,0);
	  ans0=max(ans0,dis[mx]);
	  isrt[lop[i]]=true;
     }
     for (int i=2;i<=tot;i++) sum[i]=sum[i-1]+x[i];
     for (int i=tot+1;i<=2*tot;i++) sum[i]=sum[i-1]+x[i-tot],dep[i]=dep[i-tot];
     ll ans1=1e18;
     for (int i=1;i<=tot;i++) s1.insert(make_pair(dep[i]+sum[i],i)),s2.insert(make_pair(dep[i]-sum[i],i));
     ans1=min(ans1,calc());
     for (int i=tot+1;i<=2*tot-1;i++)
     {
	  s1.erase(s1.find(make_pair(dep[i-tot]+sum[i-tot],i-tot)));
	  s2.erase(s2.find(make_pair(dep[i-tot]-sum[i-tot],i-tot)));
	  s1.insert(make_pair(dep[i]+sum[i],i));
	  s2.insert(make_pair(dep[i]-sum[i],i));
	  ans1=min(ans1,calc());
     }
     ans1=max(ans0,ans1);
     printf("%lld.%d\n",ans1>>1,(ans1&1)? 5:0);
     return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值