删边最短路
今天写题的时候做到一个非常牛的东西。
给你一个图,\(q\) 次问你如果删掉一条边,\(1\) 到 \(n\) 的最短路会变成多少。
首先搞出来 \(1\) 出发的最短路树,然后如果这条边根本不在这棵树上,显然没有任何影响。
如果在的话,我们必然要绕路了。
给出一个性质:我们选择绕的路至多经过一条原来的非树边。
简单思考一下这是为什么。首先我们假设经过了至少两条非树边。
我们任意选取其中的两条。如果这两条有一条根本就没有跨过删除的边,显然这条边是不优的。否则,因为这两条都是非树边,我们在经过一条边之后,一定存在另一个更加不劣的方式回到主干道上。
所以我们只需要对于每一条边算一下它贡献的树边区间,用一个线段树维护即可。这个贡献的区间的两个端点其实就是在 \(1,n\) 最短路树上分别与这条边两个端点的 lca。注意这两颗最短路树上的 \(1\) 到 \(n\) 的最短路必须相同。
有例题 CF1163F。
就这个题首先我们可以把 \(1\) 到 \(n\) 的最短路摘出来,然后我们称之为关键边。
不在关键边上的修改是好处理的,这里留给读者思考。
在关键边上并且改小边权也是好处理的,这里仍然留给读者思考。
下文只处理剩下的一种情况。
我们进行最终形态宏观考虑,发现最终的路径要么不变,要么不经过改的边(因为如果经过一定是走原路径更优)。
路径不变仍然是简单的。
我们给出性质,若不经过改的边,则一定经过一条不在最短路树上的边。
那么相当于对于一条非树边,我们先从 \(1\) 走到一个端点,再走过这条边,最后从另一个端点走到 \(n\)。
显然地,我们的这条路径会共用 \(1\) 到 \(n\) 初始最短路的一段前后缀(可以为空)。于是这条边的贡献是一个区间。直接线段树区间 cmin,单点查值即可。
放一下代码:
#include<bits/stdc++.h>
#define int long long
#define N 200005
#define mod 998244353
#define pii pair<int,int>
#define x first
#define y second
#define pct __builtin_popcount
#define mpi make_pair
// #define inf 2e18
using namespace std;
int T=1,n,m,q,cnt,dis[2][N],pre[2][N],id[N],nw[N],val[N];
int inf=2e18;
pii ed[N];
bool st[N],vis[N],tr[N];
struct edge{
int x,y,id;
};
vector<edge>e[N];
struct node{
int d,u;
bool operator<(const node &t)const{
return d>t.d;
}
};
struct ds{
vector<int>g[N];
int dep[N],fa[N],siz[N],son[N],top[N];
void adde(int a,int b){
g[a].push_back(b);
}
void dfs1(int u,int f){
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(auto j:g[u]){
if(j==fa[u])continue;
dfs1(j,u);
siz[u]+=siz[j];
if(siz[j]>siz[son[u]])son[u]=j;
}
}
void dfs2(int u,int f){
top[u]=f;
if(son[u])dfs2(son[u],f);
for(auto j:g[u]){
if(j==fa[u]||j==son[u])continue;
dfs2(j,j);
}
}
int get_lca(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
a=fa[top[a]];
}
return dep[a]<dep[b]?a:b;
}
}t1,t2;
void add(int a,int b,int c,int d){
e[a].push_back({b,c,d});
}
void dij(int op,int s){
memset(dis[op],0x3f,sizeof dis[op]);
memset(st,0,sizeof st);
priority_queue<node>q;
dis[op][s]=0;
q.push({dis[op][s],s});
if(!op){
while(!q.empty()){
auto t=q.top();
q.pop();
int u=t.u;
if(st[t.u])continue;
st[t.u]=1;
for(auto it:e[u]){
int j=it.x,w=it.y;
if(dis[op][j]>dis[op][u]+w){
dis[op][j]=dis[op][u]+w;
pre[op][j]=u;
q.push({dis[op][j],j});
}
}
}
int cur=n;
while(cur){
vis[cur]=1;
id[cur]=++cnt;
cur=pre[op][cur];
}
for(int i=1;i<=n;i++){
if(id[i]){
id[i]=cnt-id[i]+1;
nw[id[i]]=i;
}
}
}
else{
while(!q.empty()){
auto t=q.top();
q.pop();
int u=t.u;
if(st[t.u])continue;
st[t.u]=1;
for(auto it:e[u]){
int j=it.x,w=it.y;
if(dis[op][j]>dis[op][u]+w){
dis[op][j]=dis[op][u]+w;
pre[op][j]=u;
q.push({dis[op][j],j});
}
else if(dis[op][j]==dis[op][u]+w&&vis[u]){
pre[op][j]=u;
}
}
}
}
}
struct sgt{
int tr[N<<2],tag[N<<2];
void pushup(int u){
tr[u]=min(tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
tag[u]=inf;
if(l==r){
tr[u]=inf;
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void maketag(int u,int v){
tr[u]=min(tr[u],v);
tag[u]=min(tag[u],v);
}
void pushdown(int u){
if(tag[u]==inf)return;
maketag(u<<1,tag[u]);
maketag(u<<1|1,tag[u]);
tag[u]=inf;
}
void modify(int u,int l,int r,int L,int R,int v){
if(L>R)return;
if(l>=L&&r<=R){
maketag(u,v);
return;
}
int mid=l+r>>1;
pushdown(u);
if(L<=mid)modify(u<<1,l,mid,L,R,v);
if(R>mid)modify(u<<1|1,mid+1,r,L,R,v);
pushup(u);
}
int qry(int u,int l,int r,int L,int R){
if(L>R)return inf;
if(l>=L&&r<=R)return tr[u];
int mid=l+r>>1;
int res=inf;
pushdown(u);
if(L<=mid)res=min(res,qry(u<<1,l,mid,L,R));
if(R>mid)res=min(res,qry(u<<1|1,mid+1,r,L,R));
return res;
}
}sgt;
void solve(int cs){
if(!cs)return;
cin>>n>>m>>q;
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c,i);
add(b,a,c,i);
val[i]=c;
ed[i]=mpi(a,b);
}
dij(0,1);
dij(1,n);
for(int i=1;i<=n;i++){
for(auto it:e[i]){
int j=it.x,w=it.y,d=it.id;
if(pre[0][j]==i&&dis[0][i]+w==dis[0][j]&&id[i]&&id[j]){
tr[d]=1;
}
}
}
for(int i=2;i<=n;i++){
t1.adde(pre[0][i],i);
}
for(int i=1;i<n;i++){
t2.adde(pre[1][i],i);
}
t1.dfs1(1,0);
t1.dfs2(1,1);
t2.dfs1(n,0);
t2.dfs2(n,n);
sgt.build(1,1,cnt);
for(int i=1;i<=m;i++){
int a=ed[i].x,b=ed[i].y,c=val[i];
if(tr[i])continue;
int L=id[t1.get_lca(a,n)],R=id[t2.get_lca(1,b)];
if(L>=R)swap(a,b);
L=id[t1.get_lca(a,n)],R=id[t2.get_lca(1,b)];
sgt.modify(1,1,cnt,L,R-1,c+dis[0][a]+dis[1][b]);
}
while(q--){
int t,x;
cin>>t>>x;
if(!tr[t]){
if(x>=val[t]){
cout<<dis[0][n]<<'\n';
}
else{
int a=ed[t].x,b=ed[t].y;
cout<<min({dis[0][n],dis[0][a]+x+dis[1][b],dis[0][b]+x+dis[1][a]})<<'\n';
}
}
else{
if(x<=val[t]){
cout<<dis[0][n]-val[t]+x<<'\n';
}
else{
int a=ed[t].x,b=ed[t].y;
cout<<min(dis[0][n]-val[t]+x,sgt.qry(1,1,cnt,min(id[a],id[b]),min(id[a],id[b])))<<'\n';
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cin>>T;
// init();
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}

浙公网安备 33010602011771号