题解:
二分答案。
假设枚举到答案是
x
x
x。
找出所有长度大于
x
x
x的链,那么这些链上必须要放虫洞,而且必须放在一条所有链都经过的边上,否则必然有一条链的长度是大于
x
x
x的。
这个直接差分就能求出所有链都经过的边,并且把最长的那条改为虫洞,看看所有边是否合法,则可判断
x
x
x是否可行。
应该不是很难吧。。。
比较简单的NOIP题。
不过代码有点毒瘤,95分了n遍,最后把swap改掉才过。
Code:
#include<bits/stdc++.h>
#define ll long long
const int N=1e6+5;
using namespace std;
int dist[N],len[N],head[N],n,m,deep[N],fa[N];
int tot,t[N],f[N][20],A[N],B[N],ans[N],sum[N];
inline ll read()
{
ll x=0,f=1;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
return x*f;
}
struct node
{
int vet,next,len;
}edge[N];
void add(int u,int v,int w)
{
edge[++tot].vet=v;
edge[tot].next=head[u];
head[u]=tot;
edge[tot].len=w;
}
void dfs(int u)
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].vet;
if(v!=fa[u])
{
deep[v]=deep[u]+1;
fa[v]=u;
len[v]=edge[i].len;
dist[v]=dist[u]+len[v];
dfs(v);
}
}
}
int lca(int u,int v)
{
if(deep[u]<deep[v])u^=v^=u^=v;
for(int j=19;j>=0;j--)
if(deep[f[u][j]]>=deep[v])u=f[u][j];
if(u==v)return u;
for(int j=19;j>=0;j--)
if(f[u][j]!=f[v][j])u=f[u][j],v=f[v][j];
return fa[u];
}
void Dfs(int u)
{ for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].vet;
if(v!=fa[u])
{
Dfs(v);
sum[u]+=sum[v];
}
}
}
int check(int x)
{
int cnt=0,mx=0;
for(int i=1;i<=n;i++)sum[i]=0;
for(int i=1;i<=m;i++)
if(ans[i]>x)
{
cnt++;
mx=max(mx,ans[i]-x);
sum[A[i]]++;
sum[B[i]]++;
sum[t[i]]-=2;
}
Dfs(1);
for(int i=1;i<=n;i++)
if(sum[i]==cnt&&len[i]>=mx)return 1;
return 0;
}
int main()
{
n=read(),m=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read(),w=read();
add(u,v,w);add(v,u,w);
}
dfs(1);
for(int i=1;i<=n;i++)f[i][0]=fa[i];
for(int j=1;j<20;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
int l=0,r=0;
for(int i=1;i<=m;i++)
{
A[i]=read(),B[i]=read();
int u=A[i],v=B[i];
t[i]=lca(u,v);
ans[i]=dist[u]+dist[v]-2*dist[t[i]];
r=max(r,ans[i]);
}
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid-1;else l=mid+1;
}
printf("%d\n",l);
return 0;
}