介是你没有体验过的\(chuan\)新做法。
暴力点分治算出每个点作为中心时的答案。
对于路径\(c\rightarrow u\rightarrow c\rightarrow v\rightarrow c\)的长度除以\(2\),我们可以看做\(dis(u,v)+c\)到路径\((u,v)\)的距离乘\(2\)。
把路径分成两类:
经过分治中心的:
对于这种路径,\(dfs\)一遍树,当脱离了某条路径时,比如走边\((2,4)\)或\((2,5)\),开始计算该路径的贡献。假设走边\((x,y)\)脱离了该路径,则\(y\)子树内每个点\(i\)都会有\(dis(i,x)\times 2+dis(u,v)\)的贡献。
具体实现:给每条边维护一个权值\(V\),对每条路径上的边的\(V\)对\(dis(u,v)\)取\(\max\)。算出每个点每条出边\(V\)的最大值和次大值。\(dfs\)时维护当前最大贡献\(now\),对每个点,等于该点最大值的边选次小值更新\(now\),否则选最大值,累加边权乘\(2\)即可。
这样会发现从根节点开始\(dfs\)时会出现这种情况:
此时,红色路径和绿色路径为最大和次大。
由于一条路径可能会占据根节点的两条出边,维护最大值和次大值就出\(bug\)了。
这样我们直接维护一个堆把每条路径加进去,从根节点出发时把每条经过该点的路径删掉,统计完再加回去。
不经过分治中心的:
把\((u,v)\)的\(lca\)标记为关键点,并赋予一个权值\(mx\)为所有标记它的点对中最大的\(dis(u,v)\)。记\(ma(i)\)为\(i\)到其子树内所有关键点中(设关键点为\(p\)),最大的\(mx(p)+dis(p,i)\times 2\)。
转移很简单:\(ma(i)=\max\{mx(i),\max\limits_{edge(i,j)}\{ma(j)+edge(i,j).l\times 2\}\}\)
对根维护一个\(ma1\)和\(ma2\)表示根的儿子中最大和次大的\(ma\)。
\(dfs\)根的每个儿子\(i\)的子树,同样的,若\(ma(i)=ma1\)选\(ma2\),否则选\(ma1\)为\(now\),对每个点\(j\)的贡献就是\(now+dis(root,j)\times 2\)。
注意不用也不能在统计时进一步更新\(now\)了。
繁琐的细节:
- 分类路径时,可以巧妙运用\(tarjan\ lca\)。
- 处理边的\(V\)时,其实就是链取\(\max\)单点查。由于这些路径都经过根节点,直接对两端点取\(\max\),统计子树最大值即可。
- 直接位于路径\((u,v)\)的点算不上\((u,v)\)的贡献,需要额外处理,可以在统计\(V\)时一块算,免去了树剖两个\(\log\)或\(lct\)的大常数。
- 由于一些神奇的原因,形如\((u,u)\)的路径不会被算到,把\(u\)拆出来\(u'\)连上长为\(0\)的边,把路径改为\((u,u')\)即可。
关于正确性:
如果一条路径经过根节点,就会统计上它对当前树内所有点的贡献。
否则会统计它对和它不属于根节点同一个儿子的子树的节点的贡献,然后分治它的子树。
我们就能把它对每个点的贡献算上。
关于复杂度:
随便想一想就会发现复杂度是\(O((m+n)\log n)\)的。
代码:
我的\(priority\_queue\)不知道为啥会在程序结束时炸掉于是抄手写的堆。
尽量精简了代码还是有\(200+\)行毕竟是暴力。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <vector> #include <queue> #define maxn 200005 #define inf 0x3f3f3f3f using namespace std; inline int read(){ int x=0,y=0; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return y?-x:x; } namespace origin{ int h[maxn],num; struct edge{ int pre,to,l; }e[maxn<<1]; inline void add(int from,int to,int l){ e[++num].pre=h[from],h[from]=num,e[num].to=to,e[num].l=l; e[++num].pre=h[to],h[to]=num,e[num].to=from,e[num].l=l; } } int h[maxn],siz[maxn],top[maxn],son[maxn],deep[maxn],fa[maxn],F[maxn],qu[maxn][2],num,all,mx,root,cnt; int ma1[maxn],ma2[maxn],ema1[maxn],ema2[maxn],ans[maxn],dis[maxn],ee[maxn],bl[maxn],sp[maxn]; vector<pair<int,int> >v[maxn]; vector<int>poi[maxn]; bool vis[maxn],ap[maxn]; struct heap{ int H[maxn],heap_size; void push(int d){ int now=++heap_size; H[heap_size]=d; while(now>1){ if(H[now]<H[now>>1]) return; swap(H[now],H[now>>1]); now>>=1; } } void pop(){ int now=1,next; H[1]=H[heap_size--]; while((now<<1)<=heap_size){ next=now<<1; if(next<heap_size&&H[next+1]>H[next]) next++; if(H[now]>H[next]) return; swap(H[now],H[next]); now=next; } } bool empty(){return !(bool(heap_size));} }; struct del_heap{ heap q,d; void push(int x){q.push(x);} void del(int x){d.push(x);} void clear(){q.heap_size=d.heap_size=0;} int top(){ while(!d.empty()&&q.H[1]==d.H[1])q.pop(),d.pop(); if(q.empty())return -inf; return q.H[1]; } }q; struct edge{ int pre,to,l,v; }e[maxn<<1]; int find(int x){ if(F[x]==x)return x; return F[x]=find(F[x]); } inline void add(int from,int to,int l){ e[++num].pre=h[from],h[from]=num,e[num].to=to,e[num].l=l,e[num].v=-inf; } void dfs1(int node=1){ siz[node]=1; int x; for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(siz[x])continue; fa[x]=node,deep[x]=deep[node]+1,dis[x]=dis[node]+e[i].l; dfs1(x),siz[node]+=siz[x]; if(siz[x]>siz[son[node]])son[node]=x; } } void dfs2(int node=1){ vis[node]=1; if(son[node]){ top[son[node]]=top[node],dfs2(son[node]); int x; for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(!vis[x])top[x]=x,dfs2(x); } } } inline int lca(int x,int y){ while(top[x]!=top[y])deep[top[x]]<deep[top[y]]?y=fa[top[y]]:x=fa[top[x]]; return deep[x]<deep[y]?x:y; } inline void check(int &m1,int &m2,int x){ if(x>m1)m2=m1,m1=x; else if(x>m2)m2=x; } void getroot(int node,int f){ int x,ma=0; for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(x==f||vis[x])continue; getroot(x,node),ma=max(ma,siz[x]); } ma=max(ma,all-siz[node]); if(ma<mx)mx=ma,root=node; } void init(int node,int f,int b){ int x; bl[node]=b,ema1[node]=ema2[node]=ma1[node]=ma2[node]=-inf,ap[node]=1,F[node]=node; for(vector<pair<int,int> >::iterator iter=v[node].begin();iter!=v[node].end();++iter) if(ap[iter->first]){ x=find(iter->first); if(x!=root)ma1[x]=max(ma1[x],iter->second); else { ee[iter->first]=max(ee[iter->first],iter->second),ee[node]=max(ee[node],iter->second); ma2[node]=max(ma2[node],iter->second),poi[b].push_back(iter->second); if(iter->first!=root)poi[bl[iter->first]].push_back(iter->second),ma2[iter->first]=max(ma2[iter->first],iter->second); q.push(iter->second); } } for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(x==f||vis[x])continue; init(x,node,b),F[x]=node,ma1[node]=max(ma1[node],ma1[x]+(e[i].l<<1)); } } void calc(int node,int f,int now){ siz[node]=1,ans[node]=max(ans[node],now); int x; for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(x==f||vis[x])continue; calc(x,node,max(e[i].v==ema1[node]?ema2[node]:ema1[node],max(now,ma2[node]))+(e[i].l<<1)); siz[node]+=siz[x]; } } void calc_edge_value(int node,int f){ int x; for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(x==f||vis[x])continue; calc_edge_value(x,node); ee[node]=max(ee[node],ee[x]),check(ema1[node],ema2[node],e[i].v=ee[x]); } ans[node]=max(ans[node],ee[node]); } void clear(int node,int f){ ap[node]=0,ee[node]=-inf; int x; for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(x==f||vis[x])continue; clear(x,node),e[i].v=-inf; } } void solve(int node){ q.clear(),vis[node]=1; int x; ma1[node]=ma2[node]=-inf,F[node]=node,ap[node]=1,bl[node]=0; for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(vis[x])continue; init(x,node,x),F[x]=node,check(ma1[node],ma2[node],ma1[x]+(e[i].l<<1)); } calc_edge_value(node,0); ans[node]=max(ans[node],ma1[node]); for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(vis[x])continue; for(register int j=0;j<poi[x].size();++j)q.del(poi[x][j]); calc(x,node,max((ma1[x]+(e[i].l<<1)==ma1[node]?ma2[node]:ma1[node]),q.top())+(e[i].l<<1)); for(register int j=0;j<poi[x].size();++j)q.push(poi[x][j]); poi[x].clear(); } clear(node,0); for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(vis[x])continue; mx=inf,all=siz[x],getroot(x,node),solve(root); } } void rebuild(int node=1,int f=0,int gf=0){ int x; if(node>all)for(register int i=h[node];i;i=e[i].pre){ x=e[i].to; if(x==f)continue; rebuild(x,node,f); } else for(register int i=origin::h[node];i;i=origin::e[i].pre){ x=origin::e[i].to; if(x==f||x==gf)continue; if(sp[x])add(node,sp[x],origin::e[i].l),add(sp[x],node,origin::e[i].l),add(sp[x],x,0),add(x,sp[x],0),x=sp[x]; else add(node,x,origin::e[i].l),add(x,node,origin::e[i].l); rebuild(x,node,f); } } int DIS(int x,int y){ return dis[x]+dis[y]-(dis[lca(x,y)]<<1); } int main(){ memset(ee,~0x3f,sizeof ee); int n=all=read(),m=read(),k=n,x,y,z,res=inf; for(register int i=1;i<n;++i)x=read(),y=read(),z=read(),origin::add(x,y,z); for(register int i=1;i<=m;++i){ qu[i][0]=x=read(),qu[i][1]=y=read(); if(x==y&&!sp[x])sp[x]=++k,qu[i][1]=k; } if(sp[1])add(sp[1],1,0),add(1,sp[1],0); rebuild(1,sp[1]),dfs1(),dfs2(); for(register int i=1;i<=m;++i){ x=qu[i][0],y=qu[i][1],z=dis[x]+dis[y]-(dis[lca(x,y)]<<1); v[x].push_back((pair<int,int>){y,z}),v[y].push_back((pair<int,int>){x,z}); } memset(vis,0,sizeof vis); all=k,mx=inf,getroot(1,0),solve(root); for(register int i=1;i<=n;++i)res=min(res,ans[i]); printf("%d\n",res); }