洛谷 P9432 [NAPC-#1] rStage5 - Hard Conveyors
这道题我看大家都用 dijkstra 啊,惊恐,这里提供一种换根 dp 的写法。
两点间最短路径,那一定是 LCA 没错了。用一遍 dfs 求出根节点到每个点的距离,记为 \(dist\)。那么 \(u,v\) 间最短路径长度的就是 \(dist_u+dist_v-dist_{\operatorname{lca}(u,v)}\times 2\)。
但是题目还要求至少经过一个关键点。如果 \(u,v\) 间的路径上存在关键点,那自然是好的,直接走就可以了;若不存在,则需要找一个路径上的点,使这个点向外走到一个关键点再回来的距离最小。
形式化地说,若路径长为 \(L\),每个点 \(i\) 到关键点的最近距离为 \(F_i\),则答案 \(ans=L+\min{F_i}\times 2\),其中点 \(i\) 在 \(u\) 到 \(v\) 的路径上。
之后就是实现了。我用的树剖加换根 dp。树剖求 LCA,再加线段树维护 \(F_i\) 的区间最小值,十分好写,这里就不赘述了,主要是 \(F\) 的求法。
- 设 \(f_i\) 为以 \(i\) 点为根的子树中,点 \(i\) 与关键点的最近距离。这个十分好求,一遍 dfs 轻松解决。重要的是如何用第二次 dfs 进行换根。
- 注意到 \(f\) 只包含了子树内的情况,那么就设 \(g_i\) 为 \(i\) 的子树外的关键点距离 \(i\) 的最近距离。有一个显然的转移:$$g_v=\min(g_v,g_u+edge_{u\to v})$$ 也就是从 \(u\) 的子树外转移而来。
- 此外我们还有一种情况没有考虑,就是在 \(u\) 的子树内,但不在 \(v\) 的子树内,这时候 \(f\) 一维就不是很好转移了。我们修改为: 设 \(f_{i,1}\) 为以 \(i\) 为根的子树中,点 \(i\) 与关键点的最小距离;\(f_{i,0}\) 则为次小距离。
这样一来,若当前 \(v\) 是那个最小值,就需要从次小值转移过来;否则从最小值转移。
状态转移方程为:\(\begin{cases}g_v=\min(g_v,f_{u,0}+edge_{u\to v})&f_{v,1}+edge_{u\to v}=f_{u,1}\\g_v=\min(g_v,f_{u,1}+edge_{u\to v})&\operatorname{otherwise}\end{cases}\)
之后,显然 \(F_i=\min(g_i,f_{i,1})\),再用线段树维护最小值即可。
点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<utility>
#include<vector>
#include<queue>
#include<bitset>
#define int long long
#define FOR(i,a,b) for(register int i=a;i<=b;i++)
#define ROF(i,a,b) for(register int i=a;i>=b;i--)
#define mp(a,b) make_pair(a,b)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
inline int read();
typedef long long ll;
const int N=1e5+5;
const int INF=0x3f3f3f3f;
int n,m,k,q;
struct E{
int to,nex,w;
}edge[N<<1];
int head[N],cnt=0;
void add(int u,int v,int w){
edge[++cnt]=(E){v,head[u],w};
head[u]=cnt;
}
bitset<N>mark;
int son[N],fa[N],top[N],sz[N],dfn[N],dep[N],dist[N],F[N][2],g[N];
void dfs1(int u,int f){
sz[u]=1;
fa[u]=f;
dep[u]=dep[f]+1;
int max_son=-1;
F[u][0]=F[u][1]=INF;
if(mark[u]) F[u][0]=F[u][1]=0;
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].to;
if(v==f) continue;
dist[v]=dist[u]+edge[i].w;
dfs1(v,u);
if(F[v][1]+edge[i].w<F[u][1]){
F[u][0]=F[u][1];
F[u][1]=F[v][1]+edge[i].w;
}
else if(F[v][1]+edge[i].w<F[u][0]) F[u][0]=min(F[u][0],F[v][1]+edge[i].w);
sz[u]+=sz[v];
if(max_son<sz[v]) max_son=sz[v],son[u]=v;
}
}
int tot=0,pos[N];
void dfs2(int u,int topx){
dfn[u]=++tot;
pos[tot]=min(F[u][1],g[u]);
top[u]=topx;
if(!son[u]) return ;
dfs2(son[u],topx);
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].to;
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int tr[N<<2];
void pushup(int u){
tr[u]=min(tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
if(l==r) return tr[u]=pos[l],void();
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
int query(int u,int l,int r,int L,int R){
if(L<=l&&r<=R)
return tr[u];
int mid=l+r>>1;
int res=INF;
if(L<=mid) res=min(res,query(u<<1,l,mid,L,R));
if(R>mid) res=min(res,query(u<<1|1,mid+1,r,L,R));
return res;
}
pii LCA(int u,int v){
int res=INF;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res=min(res,query(1,1,n,dfn[top[u]],dfn[u]));
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
res=min(res,query(1,1,n,dfn[u],dfn[v]));
return mp(u,res);
}
void dfs3(int u,int f){
if(mark[u]) g[u]=0;
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].to;
if(v==f) continue;
g[v]=min(g[v],g[u]+edge[i].w);
if(F[v][1]+edge[i].w==F[u][1]){
g[v]=min(g[v],F[u][0]+edge[i].w);
}
else g[v]=min(g[v],F[u][1]+edge[i].w);
dfs3(v,u);
}
}
signed main()
{
n=read(),q=read(),k=read();
FOR(i,1,n-1){
int u=read(),v=read(),w=read();
add(u,v,w);
add(v,u,w);
}
FOR(i,1,k){
mark[read()]=1;
}
dfs1(1,0);
memset(g,0x3f,sizeof g);
g[1]=F[1][1];
dfs3(1,0);
dfs2(1,1);
build(1,1,n);
while(q--){
int s=read(),t=read();
if(s==t){
printf("%lld\n",min(F[s][1],g[s])*2);
continue;
}
pii temp=LCA(s,t);
int lca=temp.fi,num=temp.se;
int ans=dist[s]+dist[t]-dist[lca]*2+num*2;
printf("%lld\n",ans);
}
return 0;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return f*x;
}
$\color{Red}\text{心怀荣耀,勇往直前。}$

浙公网安备 33010602011771号