1. 程式人生 > 其它 >在固定起終點的最短路上修改一條邊的邊權

在固定起終點的最短路上修改一條邊的邊權

轉換思路很nb。

設修改的邊為 \((x,y)\),邊權為 z
不妨設起點為 1,終點為 n。
建出最短路徑樹,搞出 1-n 的一條鏈(下文簡稱“鏈”)。
記錄鏈經過的邊分別為 \(a_i(i\in[1,邊數])\)
預處理 1 到所有點的最短距離 \(dis_{1,i}\) 和 n 到所有點的最短距離 \(dis_{n,i}\)
考慮對修改的邊進行分類:

  1. 修改邊不在鏈上
    明顯答案為 \(min\{dis_{1,n},dis_{1,x}+z+dis_{y,n},dis_{1,y}+z+dis_{x,n}\}\)

  2. 修改邊在鏈上
    不妨假設最短路徑 1->n 中為 x->y
    考慮從圖上刪除這條邊再求最短路。
    這等價於求出對於所有 \(u\in[1,x]\)

    \(v\in[y,n]\),先沿鏈 1->u,再離開鏈 u->v,最後沿鏈 v->n 的合法路徑的長度最小值。
    (此處的 \([1-x]、[y-n]\) 均為鏈上點)

    假設 c 為起點,v 為終點,最短路徑為 c->x->y->v
    那麼 c->x,x->b,b->w,w->y,y->v 就是合法的。

    這玩意看起來非常難搞,複雜度起飛。想想辦法轉化一下。
    考慮非鏈邊對此的影響。
    若我們強制經過一條非鏈邊 \((u,v,w)\),則貢獻為\(min\{dis_{1,u}+w+dis_{v,n},dis_{1,v}+w+dis_{u,n}\}\)


    考慮哪些鏈邊能夠獲得此貢獻。
    設 1->u 經過的鏈上的邊的最大編號為 a,v->n 經過的鏈上的邊的最小編號為 b
    設 a 的相對靠近 n 的端點為 p,b 的相對靠近 1 的端點為 q。
    結論:\((u,v,w)\)對編號為\(i\in[a+1,b-1]\)的邊有貢獻
    證明:顯然
    手摸可以發現並不會在 p->q 的過程中經過鏈上的邊。
    反證:設 p->r->s->q,為最優方式,其中r->s為鏈邊。
    若 p->r->s->q 更優,則應有最短路1->p->r->s->q->n而非原鏈,證畢。
    那麼鏈上從p到q的邊都都可以被更新。
    如果有等價路徑則儘量離開鏈。

    實現中這玩意其實不一定是一段連續前後綴
    比如這樣:

    在有多條最短路徑時可以看到不一定要沿著鏈走。
    比如考慮經過\((6,8,2)時\) 1->2->4->5->6->8 可行。
    所以才採取最大a,最小b的形式。


code

no such code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct edge{
	int y,z,nxt;
}e[400010];
int head[200010],cnt;
ll dis[200010];
ll dis1[200010];
ll dis2[200010];
struct data{
	int x;
	bool operator <(const data &v)const{
		return dis[x]>dis[v.x];
	}
};
priority_queue<data>q;
int in[400010];
bool inq[200010];
int pr1[200010];
int pr2[200010];
int fr[200010];
ll tree[800010];
ll laz[800010];
int n,m,Q,len;
ll ans;
void add(int x,int y,int z){
	cnt++;
	e[cnt].y=y;
	e[cnt].z=z;
	e[cnt].nxt=head[x];
	head[x]=cnt;
}
void dij1(){
	for(int i=2;i<=n;i++)
	dis[i]=2e18;
	q.push((data){1});
	inq[1]=true;
	while(q.size()){
		int x=q.top().x; 
		q.pop(),inq[x]=0;
		for(int i=head[x];i;i=e[i].nxt){
			int y=e[i].y,z=e[i].z;
			if(dis[y]>dis[x]+z){
				dis[y]=dis[x]+z;
				fr[y]=i;
				if(inq[y]==0){
					q.push((data){y});
					inq[y]=true;
				}
			}
		}
	}
	for(int i=1;i<=n;i++)
	dis1[i]=dis[i];
	ans=dis[n];
	int x=n;
	while(x!=1){
		//cout<<x<<" ";
		int i=fr[x]&1?fr[x]+1:fr[x]-1;
		in[fr[x]]=in[i]=++len;
		x=e[i].y;
	}
	//cout<<1<<endl<<endl;
}
void dij2(){
	for(int i=1;i<=n;i++)
	dis[i]=2e18,pr2[i]=n+1;
	dis[n]=0;
	pr2[n]=0;
	q.push((data){n});
	inq[n]=true;
	while(q.size()){
		int x=q.top().x; 
		q.pop(),inq[x]=0;
		for(int i=head[x];i;i=e[i].nxt){
			int y=e[i].y,z=e[i].z;
			if(dis[y]>dis[x]+z||(dis[y]==dis[x]+z&&pr2[y]>max(pr2[x],in[i]))){
				dis[y]=dis[x]+z;
				pr2[y]=max(pr2[x],in[i]);
				if(inq[y]==0){
					q.push((data){y});
					inq[y]=true;
				}
			}
		}
	}
	for(int i=1;i<=n;i++)
	dis2[i]=dis[i];/*,cout<<pr2[i]<<" ";cout<<endl;
	for(int i=1;i<=n;i++)
	cout<<dis2[i]<<" ";
	cout<<endl<<endl;*/
}
void dij3(){
	for(int i=1;i<=n;i++)
	dis[i]=2e18;
	dis[1]=0;
	pr1[1]=len+1;
	q.push((data){1});
	inq[1]=true;
	while(q.size()){
		int x=q.top().x; 
		q.pop(),inq[x]=0;
		for(int i=head[x];i;i=e[i].nxt){
			int y=e[i].y,z=e[i].z;
			if(dis[y]>dis[x]+z||(dis[y]==dis[x]+z&&pr1[y]<min(pr1[x],in[i]==0?n+1:in[i]))){
				dis[y]=dis[x]+z;
				pr1[y]=min(pr1[x],in[i]==0?n+1:in[i]);
				if(inq[y]==0){
					q.push((data){y});
					inq[y]=true;
				}
			}
		}
	}
	/*for(int i=1;i<=n;i++)
	cout<<pr1[i]<<" ";cout<<endl;
	for(int i=1;i<=n;i++)
	cout<<dis1[i]<<" ";
	cout<<endl<<endl;*/
}
void pushdown(int id){
	if(laz[id]!=2e18){
		tree[id<<1]=min(tree[id<<1],laz[id]);
		tree[id<<1|1]=min(tree[id<<1|1],laz[id]);
		laz[id<<1]=min(laz[id<<1],laz[id]);
		laz[id<<1|1]=min(laz[id<<1|1],laz[id]);
		laz[id]=2e18;
	}
}
void update(int id,int l,int r,int ul,int ur,ll x){
	if(ul<=l&&r<=ur){
		tree[id]=min(tree[id],x);
		laz[id]=min(laz[id],x);
		return;
	}
	pushdown(id);
	int mid=(l+r)>>1;
	if(ul<=mid) update(id<<1,l,mid,ul,ur,x);
	if(ur>mid) update(id<<1|1,mid+1,r,ul,ur,x);
	tree[id]=min(tree[id<<1],tree[id<<1|1]);
}
ll query(int id,int l,int r,int d){
	if(l==r) return tree[id];
	int mid=(l+r)>>1;
	pushdown(id);
	if(d<=mid) return query(id<<1,l,mid,d);
	else return query(id<<1|1,mid+1,r,d);
}
int main(){
	int x,y,z;
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z);
	}
	//cout<<endl;
	dij1(),dij2(),dij3();
	//cout<<endl;
	for(int i=1;i<=4*len;i++)
	tree[i]=laz[i]=2e18; 
	for(int i=1;i<=2*m;i+=2)
	if(in[i]==0){
		x=e[i].y,y=e[i+1].y,z=e[i].z;
		//cout<<x<<" "<<y<<" "<<z<<" "<<pr1[x]<<" "<<pr2[x]<<" "<<pr1[y]<<" "<<pr2[y]<<" "<<dis1[x]<<" "<<dis2[x]<<" "<<dis1[y]<<" "<<dis2[y]<<endl;
		if(pr2[x]+1<=pr1[y]-1) update(1,1,len,pr2[x]+1,pr1[y]-1,dis2[x]+dis1[y]+z);
		if(pr2[y]+1<=pr1[x]-1) update(1,1,len,pr2[y]+1,pr1[x]-1,dis2[y]+dis1[x]+z);
	}
	//cout<<endl;
	for(int i=1;i<=Q;i++){
		scanf("%d%d",&x,&y);
		int u=e[2*x].y;
		int v=e[2*x-1].y;
		int w=e[2*x].z;
		//cout<<u<<" "<<v<<" "<<w<<endl;
		if(in[2*x]) printf("%lld\n",min(ans-w+y,query(1,1,len,in[2*x])));
		else printf("%lld\n",min(ans,min(dis1[u]+dis2[v]+y,dis1[v]+dis2[u]+y)));
	}
}