一个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; }