专栏文章

题解:P11898 移动迷宫

P11898题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipw1m9m
此快照首次捕获于
2025/12/03 18:52
3 个月前
此快照最后确认于
2025/12/03 18:52
3 个月前
查看原文

分析

一道很不错的分层图最短路题目。
观察 (或打表) 可以发现道路的长度每三个一循环,即 111111x=x\frac{1}{1-\frac{1}{1-\frac{1}{1-x}}}=x
那么可以对原图建三层的分层图,节点 xx 对应的三个节点 x1=x,x2=x+n,x3=x+n×2x_1=x,x_2=x+n,x_3=x+n\times 2。若原图有一条从 uuvv,长度长度为 ww 的边,w1=w,w2=11w1,w3=11w2w_1=w,w_2=\frac{1}{1-w_1},w_3=\frac{1}{1-w_2},则建好的分层图如下:
分层图示意图
在分层图上跑最短路,最后输出节点 nn 对应的三个节点 n1,n2,n3n_1,n_2,n_3 距离 11 号节点的距离的最小值即可。
好像没有卡 SPFA。

Code

CPP
#include<bits/stdc++.h>
#define cst const
#define i64 long long
#define mmax(x,y) ((x)>(y)?(x):(y))
#define mmin(x,y) ((x)<(y)?(x):(y))

using namespace std;

template<typename T>
void read_arr(int n,T *arr){
	for(int i=1;i<=n;++i)cin>>arr[i];
	return;
}

namespace Math{
	i64 ExGcd(cst i64 &a,cst i64 &b,i64 &x,i64 &y){
		if(b==0){x=1,y=0;return a;}
		
		i64 res=ExGcd(b,a%b,y,x);
		y-=a/b*x;
		return res;
	}
	
	//返回值为 a/b%m,本题中模数为质数,可以直接用快速幂,但扩欧更保险
	i64 getmod_frac(cst i64 &a,cst i64 &b,cst i64 &m){
		i64 x=0,y=0;
		i64 tmp=Math::ExGcd(b,m,x,y);
		x=x*a/tmp,x=(x%m+m)%m;
		
		if(a%tmp)return -1;
		return x;
	}
}

constexpr int N=1e5+5;
constexpr i64 mod=754974721;
int n,m;

namespace Graph{
	int tot_edge,hd[N*3];
	struct Edge{int to;i64 val;int lst;}g[N*12];
	void add_edge(cst int &u,cst int &v,cst i64 &w){
		g[++tot_edge]=Edge{v,w,hd[u]};
		hd[u]=tot_edge;
	}
	
	//最短路
	i64 dist[N*3];bool inque[N*3];
	queue<int> que;
	void spfa(int st){
		memset(dist,0x3f,sizeof dist),
		memset(inque,false,sizeof inque);
		while(!que.empty())que.pop();
		
		dist[st]=0,inque[st]=true;
		que.push(st);
		
		while(!que.empty()){
			int frt=que.front();que.pop();
			
			for(int i=hd[frt];~i;i=g[i].lst){
				int ne=g[i].to;i64 val=g[i].val;
				
				if(dist[ne]<=dist[frt]+val)continue;
				dist[ne]=dist[frt]+val;
				if(inque[ne]==false){
					inque[ne]=true;
					que.push(ne);
				}
			}
			
			inque[frt]=false;
		}
	}
}

using namespace Graph;

int main(){
	ios::sync_with_stdio(false),
	cin.tie(nullptr),cout.tie(nullptr);
	
	memset(hd,-1,sizeof hd);
	
	cin>>n>>m;
	for(int _=0;_<m;++_){
		int x1,x2;i64 w1;cin>>x1>>x2>>w1;
		
		//求边权
		i64 w2=Math::getmod_frac(1,1-w1,mod);
		i64 w3=Math::getmod_frac(1,1-w2,mod);
		
		//建分层图
		add_edge(x1+n*0,x2+n*1,w1),add_edge(x2+n*0,x1+n*1,w1),
		add_edge(x1+n*1,x2+n*2,w2),add_edge(x2+n*1,x1+n*2,w2),
		add_edge(x1+n*2,x2+n*0,w3),add_edge(x2+n*2,x1+n*0,w3);
	}
	
	spfa(1);
	
	i64 ans=1e18;
	for(int i=0;i<=2;++i)ans=mmin(ans,dist[n+n*i]);
	
	cout<<ans<<endl;
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...