二分答案+LCA+树上差分
最好用Tarjan求LCA,有的oj会卡倍增。
做法:
我们用LCA求出需要查询的每个计划的路径长度。
然后二分答案,check( )的时候,我们把大于mid的路径(因为这一些都是要去边的)求一下交点(边),
如果并非全都都交于一条边或者去掉交边后也不能让这些路径都小于等于mid,那么mid就是不可以的。
求交边时,用到树上差分。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int N=300009;
int head[N],nxt[2*N],to[2*N],w[2*N],tot;
int qh[N],qn[2*N],qt[2*N],qtt,lca[2*N];
bool vis[N];
int f[N],dep[N],a[N],b[N],dis[N],s[N],v[N];
int n,m;
void add(int x,int y,int z)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
w[tot]=z;
}
void add2(int x,int y)
{
qt[++tot]=y;
qn[tot]=qh[x];
qh[x]=tot;
}
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
void dfs(int x)//Tarjan找LCA
{
vis[x]=1;
f[x]=x;
for(int i=head[x];i;i=nxt[i])
{
if(!vis[to[i]])
{
dep[to[i]]=dep[x]+w[i];
dfs(to[i]);
f[to[i]]=x;
v[to[i]]=w[i];
}
}
for(int i=qh[x];i;i=qn[i])
{
if(vis[qt[i]])
{
lca[i]=find(qt[i]);
if(i%2) lca[i+1]=lca[i];
else lca[i-1]=lca[i];
}
}
}
void sum(int father,int x)//树上差分
{
for(int i=head[x];i;i=nxt[i])
{
if(to[i]!=father)
{
sum(x,to[i]);
s[x]+=s[to[i]];//s[x]表示的是x这个点与它的儿子的边在路径中出现的次数
}
}
}
bool check(int x)
{
int cnt=0,d=0;//d表示需要去掉的最大长度
memset(s,0,sizeof(s));
for(int i=1;i<=m;i++)
{
if(dis[i]>x){
cnt++;
d=max(d,dis[i]-x);
s[a[i]]++;
s[b[i]]++;
s[lca[i*2]]-=2;//树上差分
}
}
sum(1,1);
for(int i=1;i<=n;i++)
if(s[i]==cnt&&v[i]>=d) return 1;
return 0;
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);add(y,x,z);
}
tot=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a[i],&b[i]);
add2(a[i],b[i]);add2(b[i],a[i]);
}
dfs(1);
for(int i=1;i<=m;i++)
dis[i]=dep[a[i]]+dep[b[i]]-2*dep[lca[i*2]];//第i个计划两个城市的距离
int L=0,R=N*1000,mid;
while(L<=R)
{
mid=(L+R)>>1;
if(check(mid)) R=mid-1;
else L=mid+1;
}
printf("%d\n",L);
return 0;
}