CF1932G Moving Platforms

题目传送门

CF1932G Moving Platforms

题意

一个图,你从 1 1 1 号点出发,求到 n n n 号点的最短路。

对于每个点,有一个初始高度,每个单位时间会变化某个定值。

对于一条边 ( i , j ) (i,j) (i,j),当 h i = h j {h_i}={h_j} hi=hj 时,才可以从 i i i 走到 j j j

思路

最短路很明显可以用 dijkstra 解决,我们考虑高度如何处理。

一条边 ( i , j ) (i,j) (i,j),当 ( l i + s i ) ≡ ( l j + s j ) ( m o d H ) ({l_i}+{s_i}) \equiv ({l_j}+{s_j}) \pmod H (li+si)(lj+sj)(modH) 时满足要求。

为满足题目要求,我们设在 x x x 秒时 l i = l j l_i=l_j li=lj

∴ l i + x × S i ≡ l j + x × S j ( m o d H ) \therefore l_i + x \times S_i \equiv l_j + x \times S_j \pmod H li+x×Silj+x×Sj(modH)

∴ l i + x × S i + y × H = l j + x × S j \therefore l_i + x \times S_i + y \times H = l_j + x \times S_j li+x×Si+y×H=lj+x×Sj

∴ x × ( S i − S j ) + y × H = l j − l i \therefore x \times (S_i - S_j) + y \times H = l_j - l_i x×(SiSj)+y×H=ljli

然后我们就可以用 exgcd 求了,这里讲一下 exgcd 的执行过程。

首先求出一组解 x 0 , y 0 x_0 , y_0 x0,y0,设 k = g c d ( S i − S j , H ) k = gcd(S_i - S_j,H) k=gcd(SiSj,H)

那么我们就可以求出通解 x = x 0 + m × H k , y = y 0 + m × H k x = x_0 + m \times \dfrac{H}{k} ,y = y_0 + m \times \dfrac{H}{k} x=x0+m×kH,y=y0+m×kH,其中 m m m 为一个整数。

那么当 x > d i s u x > dis_u x>disu 时,即可通过,然后就可以求出最快的可通过的时刻。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
inline int read(){
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int maxn=2e5+5;
int t,n,m,h;
int a[maxn],l[maxn],s[maxn],d[maxn],v[maxn];
vector<int> g[maxn];
struct node{
	int now,dis;
	bool operator<(node x) const{
		return x.dis<dis;
	}
};
int exgcd(int a,int b,int &x,int &y){
	if(!b) return x=1,y=0,a;
	int d=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}
void solve(){
	n=read(),m=read(),h=read();
	for(int i=1;i<=n;i++) g[i].clear(),d[i]=2e18,v[i]=0;
	for(int i=1;i<=n;i++) l[i]=read();
	for(int i=1;i<=n;i++) s[i]=read();
	for(int i=1,u,v;i<=m;i++) u=read(),v=read(),g[u].push_back(v),g[v].push_back(u);
	priority_queue<node> q;
	q.push({1,0}),d[1]=0;
	while(q.size()){
		int f=q.top().now;
		q.pop();
		if(v[f]) continue;
		v[f]=1;
		for(auto i:g[f]){
			int w,x,y,g=exgcd((s[f]-s[i]+h)%h,h,x,y),t;
			if((l[i]-l[f])%g!=0) continue;
			x=x*(l[i]-l[f])/g,t=abs(h/g),x=(x%t+t)%t;
			if(d[f]>x) x=x+(d[f]-x+t-1)/t*t;
			if(d[i]>x+1) d[i]=x+1,q.push({i,d[i]});
		}
	}
	if(d[n]>=2e18) cout<<-1<<endl;
	else cout<<d[n]<<endl;
}
signed main(){
	t=read();
	while(t--) solve();
	return 0;
}

提交记录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值