二分+树链剖分求LCA+差分
要求最远的值最小,于是我们可以考虑二分答案。
对于每一个二分出来的lim,我们只需要判断所有路径(指运输计划里面所有的路径)中长度大于lim的所有路径是否存在一条公共边,使得最长路径减去它可以小于等于lim(如果最长的可以,那么短的也肯定可以)
那么怎么求这条边?我刚开始的想法是树剖+线段树,对每一条超过lim的路径上的边的都记+1,然后暴力枚举边来找公共边。但是这样是 O(nlog3n) 不太科学。。。
实际上可以根本不线段树,维护一个树上差分数组f即可,值也是照样加。。。预处理所有LCA,复杂度就是 O(nlogn)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define reg register
using namespace std;
struct edge{int next,to,val;}e[N<<1];int ecnt;
int n, m, last[N], fa[N], dep[N], siz[N], top[N], son[N], u[N], v[N], d[N], dis[N], Lca[N], f[N], mxlen, mx, tot, b[N];
int in()
{
reg int r=0;
reg char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')r=r*10+c-'0',c=getchar();
return r;
}
void addedge(int a, int b, int c)
{
e[++ecnt]=(edge){last[a],b,c};
last[a]=ecnt;
}
void dfs1(int x)
{
dep[x]=dep[fa[x]]+1;
siz[x]=1;
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(y==fa[x])continue;
fa[y]=x;
dis[y]=dis[x]+e[i].val;
dfs1(y);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]])
son[x]=y;
}
}
void dfs2(int x)
{
if(son[fa[x]]==x)top[x]=top[fa[x]];
else top[x]=x;
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(y!=fa[x])
dfs2(y);
}
}
int lca(int a, int b)
{
while(top[a]!=top[b])
{
if(dep[top[a]]>dep[top[b]])
a=fa[top[a]];
else b=fa[top[b]];
}
return dep[a]>dep[b]?b:a;
}
void make(int i)
{
f[u[i]]++;
f[v[i]]++;
f[Lca[i]]-=2;
}
void find(int x)
{
b[x]=f[x];
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(y==fa[x])continue;
find(y);
b[x]+=b[y];
if(b[y]==tot)
mx=max(mx,e[i].val);
}
}
bool check(int lim)
{
tot=0;
memset(f,0,sizeof(f));
for(int i = 1; i <= m; i++)
if(d[i]>lim)
{
++tot;
make(i);
}
mx=0;
find(1);
return mxlen-mx<=lim;
}
int main()
{
n=in(), m=in();
for(int i = 1; i < n; i++)
{
int a=in(), b=in(), t=in();
addedge(a,b,t);
addedge(b,a,t);
}
dfs1(1);
dfs2(1);
for(int i = 1; i <= m; i++)
{
u[i]=in(), v[i]=in();
d[i]=dis[u[i]]+dis[v[i]]-2*dis[Lca[i]=lca(u[i],v[i])];
mxlen=max(mxlen,d[i]);
}
int l = max(0,mxlen-1000), r = mxlen;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d\n",l);
}