一个O(n)的解法。
不难发现有如下性质:
- 被改造成虫洞的边一定在最长路径上
那么,我们用类似提直径的方法把这条路径给拎出来
就会形成这样的一棵树。
那么,对于一条边,若其被改成虫洞,最长路径会有如下三种情况:
-
依然是当前的最长路径
-
被改造边左端点的最长路径
-
被改造边右端点的最长路径
难道我们不用考虑经过该边的路径吗??
当然不用。
我们已经是在最长路径上改造边了,而最长路径是长于任何一条路径的(废话),那么经过该边的任何路径在此边被改造后依然短于最长路径,故不用考虑。
至于求两端的最长路径,我们可以前缀和后缀分别维护一下。
那么怎么求前缀(后缀)呢?
我们可以在每个点开个vector,对于一条路径,我们把另一端点以及路径的编号加到vector中。
然后,再开个vis数组,表示这个点是否被访问过。
在遍历时,我们访问所有以这一点为一端点的路径标号(id)和另一端点(y),若另(vis[y]=true),就拿(id)的长度去更新即可。(口胡不清,这最好手动模拟一下)
至于求路径长和LCA呢?
额,树剖我是当常数看的。。。
实在不行你可以Tarjan离线预处理啊
代码(巨丑无比):
#include<bits/stdc++.h>
#define reg register int
#define MAXN 300010
using namespace std;
int n,m,head[MAXN],dis[MAXN],tot,maxx,bh,pre[MAXN],sumfr[MAXN],sumla[MAXN],num[MAXN],lian[MAXN],ans=2e9+7;
bool mark[MAXN],vis[MAXN];
struct node {
int st,ed,lca,d;
} P[MAXN];
struct Edge {//前向星
int ed,v,last;
} G[MAXN*2];
struct que {
int ed,id;
};
vector<que> Q[MAXN];
struct s__ {//树剖预处理
int son[MAXN],size[MAXN],top[MAXN],deep[MAXN];
void DFS1(int x,int fa,int v) {
dis[x]=dis[fa]+v;
pre[x]=fa;
deep[x]=deep[fa]+1;
size[x]=1;
for(int i=head[x]; ~i; i=G[i].last) {
int t=G[i].ed,v=G[i].v;
if(t==fa)continue;
DFS1(t,x,v);
size[x]+=size[t];
if(size[son[x]]<size[t])son[x]=t;
}
}
void DFS2(int x,int fa,int zu) {
top[x]=zu;
if(son[x])DFS2(son[x],x,zu);
for(int i=head[x]; ~i; i=G[i].last) {
int t=G[i].ed;
if(t==son[x]||t==fa)continue;
DFS2(t,x,t);
}
}
int LCA(int x,int y) {
while(top[x]!=top[y]) {
if(deep[top[x]]<deep[top[y]])swap(x,y);
x=pre[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
return x;
}
} shupou;
void Rd(int &res) {//读优
res=0;
char ch=getchar();
while('0'>ch||ch>'9')ch=getchar();
while('0'<=ch&&ch<='9')res=(res<<3)+(res<<1)+(ch-'0'),ch=getchar();
}
void Add(int st,int ed,int v) {
tot++;
G[tot]=Edge {ed,v,head[st]};
head[st]=tot;
}
void DFS(int x,int fa,int &bb) {
num[x]=bb;
for(int i=head[x]; ~i; i=G[i].last) {
int t=G[i].ed;
if(t==fa)continue;
if(!mark[t])continue;
lian[bb]=G[i].v;
bb++;
DFS(t,x,bb);
}
}
void DFSla(int x,int fa,int zu) {//后缀
vis[x]=1;
for(int i=0; i<Q[x].size(); i++) {//访问vector
int t=Q[x][i].ed,id=Q[x][i].id;
if(vis[t]==1)sumla[zu]=max(sumla[zu],P[id].d);//更新
}
int nex=0;
for(int i=head[x]; ~i; i=G[i].last) {
int t=G[i].ed,v=G[i].v;
if(t==fa)continue;
if(mark[t]) {//优先遍历两旁伸出的子树
nex=t;
continue;
}
DFSla(t,x,zu);
}
if(nex) {
sumla[num[nex]]=sumla[num[x]];//更新下一个的后缀
if(nex==P[bh].st)return;//下一个点如果是另一端点的话就直接退出
DFSla(nex,x,num[nex]);
}
}
void DFSfr(int x,int fa,int zu) {//前缀
vis[x]=1;
for(int i=0; i<Q[x].size(); i++) {//访问vector
int t=Q[x][i].ed,id=Q[x][i].id;
if(vis[t]==1)sumfr[zu]=max(sumfr[zu],P[id].d);//更新
}
int nex=0;
for(int i=head[x]; ~i; i=G[i].last) {
int t=G[i].ed,v=G[i].v;
if(t==fa)continue;
if(mark[t]) {//优先遍历两旁伸出的子树
nex=t;
continue;
}
DFSfr(t,x,zu);
}
if(nex) {
sumfr[num[nex]]=sumfr[num[x]];//更新下一个的前缀
if(nex==P[bh].ed)return;//下一个点如果是另一端点的话就直接退出
DFSfr(nex,x,num[nex]);
}
}
int main() {
memset(head,-1,sizeof(head));
Rd(n),Rd(m);
for(int i=1; i<=n-1; i++) {
int x,y,z;
Rd(x),Rd(y),Rd(z);
Add(x,y,z);
Add(y,x,z);
}
shupou.DFS1(1,0,0);
shupou.DFS2(1,0,1);
for(int i=1; i<=m; i++) {
Rd(P[i].st),Rd(P[i].ed);
P[i].lca=shupou.LCA(P[i].st,P[i].ed);
P[i].d=dis[P[i].st]+dis[P[i].ed]-2*dis[P[i].lca];
if(P[i].d>maxx)maxx=P[i].d,bh=i;
Q[P[i].st].push_back(que {P[i].ed,i});
Q[P[i].ed].push_back(que {P[i].st,i});
}
int st=P[bh].st,ed=P[bh].ed,lca=P[bh].lca;
while(st!=lca)mark[st]=true,st=pre[st];
while(ed!=lca)mark[ed]=true,ed=pre[ed];
mark[lca]=true;
int bb=1;
DFS(P[bh].st,0,bb);
DFSfr(P[bh].st,0,num[P[bh].st]);
memset(vis,false,sizeof(vis));
DFSla(P[bh].ed,0,num[P[bh].ed]);
for(int i=1;i<=bb;i++){//更新最终答案
int res=0;
res=max(res,P[bh].d-lian[i]);
res=max(res,sumfr[i]);
res=max(res,sumla[i+1]);
ans=min(ans,res);
}
cout<<ans;
return 0;
}