图论题【1】
题意
input
5
1 2
2 3
3 4
4 5
output
1
限制与约定
\(3≤n≤200000\)
题解
如果只放一个点,很显然就是放在直径的中点上面,这样一定是最优的,
\[Ans=(len(直径)+1)/2$$而现在题目要求取两个点,
我们想象在两点路径的中点及其子树到两点的路径均相等,
而在中点右边(没有中点一样)到右边选的点的距离一定小于到左边选的点的距离。
当然,左边同理。
即现在题目要求断开一条边,然后使得两个部分的最长直径最短。
而断开这条边的位置,一定是在原来那棵树的直径上面。
于是呢,就把直径抽出来,把直径上每个点及其子树(子树不包括直径上其他点)
都记录一个最长直径,以及以这个点开始能走的最长距离与次长距离。
求出断开每一条边的左半部分的直径分别是多少,
在处理一遍右边的,组合一下就好了
## code
```cpp
#include <bits/stdc++.h>
#define re register int
#define inf 1e9
using namespace std;
inline void read(int &x){
x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(; isdigit(ch);ch=getchar())
x=(x<<1)+(x<<3)+(ch^48);
}
inline void print(int x){if(x>9)print(x/10);putchar((x%10)+48);}
const int N=200010;
int n,q,f[N],to[N<<1],nx[N<<1],h[N],d[N],c,v[N],l[N][4],g[N],dis[N],ans=N;
inline void add(int u,int v){to[++c]=v,nx[c]=h[u],h[u]=c;}
inline void dfs(int x,int fa){
f[x]=fa,d[x]=d[fa]+1;
for(re i=h[x];i;i=nx[i])
if(to[i]!=fa)dfs(to[i],x);
}
inline void get(int x,int fa){
l[x][1]=l[x][2]=l[x][3]=0;
for(re i=h[x];i;i=nx[i])
if(to[i]!=fa&&!v[to[i]]){
get(to[i],x);
re tmp=l[to[i]][1]+1;
if(tmp>=l[x][1])
l[x][2]=l[x][1],l[x][1]=tmp;
else if(tmp>l[x][2])l[x][2]=tmp;
l[x][3]=max(l[to[i]][3],(l[x][1]+l[x][2]));
}
}
signed main(){
freopen("ob.in","r",stdin);
freopen("ob.out","w",stdout);
read(n);int x,y,l1=0,l2=0;
for(re i=1;i<n;++i)
read(x),read(y),add(x,y),add(y,x);
dfs(1,0);
for(re i=1;i<=n;++i) if(d[i]>d[l1]) l1=i;
dfs(l1,0);
for(re i=1;i<=n;++i) if(d[i]>d[l2]) l2=i;
for(re i=l2;i;i=f[i]) v[i]=1;
for(re i=l2;i;i=f[i]) get(i,0);
for(re i=l2;i;i=f[i]){
g[f[i]]=i;
re tmp=l[g[i]][1]+1;
if(g[i]==0) tmp=0;
if(tmp>=l[i][1])
l[i][2]=l[i][1],l[i][1]=tmp;
else if(tmp>l[i][2])l[i][2]=tmp;
dis[i]=l[i][3]=max(l[g[i]][3],(l[i][1]+l[i][2]));
}
for(re i=l2;i;i=f[i]) get(i,0);
for(re i=l1;i;i=g[i]){
re tmp=l[f[i]][1]+1;
if(f[i]==0) tmp=0;
if(tmp>=l[i][1])
l[i][2]=l[i][1],l[i][1]=tmp;
else if(tmp>l[i][2])l[i][2]=tmp;
l[i][3]=max(l[f[i]][3],(l[i][1]+l[i][2]));
ans=min(ans,max(l[i][3],dis[g[i]]));
}
print((ans+1)>>1);
return 0;
}
```\]