洛谷 P4886 快递员
介是你没有体验过的\(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);
}