[浅谈] 同余最短路

总述

所谓同余最短路,就是把余数相同的情况归为一类,然后找到形成这种情况的最短路径。

P3403 跳楼机

我们假设只能跳 x 步。那么可以达到的楼层是 x,2x,3x,4x ,他们的共同点是 %x=0

那么现在再加个可以跳 y 步(若 y 不是 x 的倍数且 y<x),那么我们可以跳到 x+y,2x+y,3x+y%x=y 的点。或许还有更多种类,因此我们可以把对 x 取余相同的数归为一类,找到这类数的最小数,那么这类数大于等于最小数的数都可以表示了。

我们可以把每类数看做一个点,假设目前点为 u ,那么我们跳 y 层时,相当于到了 (u+y) 点,然后最小层数用权值求。我们便可以建一条边 (u,(y+u)%x,y)

以此类推,建完所有边后,跑一边 djk,那么每类数的最小值知道,有多少个就知道,答案就知道了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+110;
int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
int h,x,y,z;
int dis[N],head[N],to[N*5],last[N*5],w[N*5],tot;
void add(int u,int v,int t){to[++tot]=v,w[tot]=t,last[tot]=head[u],head[u]=tot;return;}
struct node{
	int dis,u;
	bool operator<(const node A)const{return A.dis<dis;}
};
void dijkstra(){
	memset(dis,0x3f,sizeof(dis));dis[0]=0;
	priority_queue<node>q;q.push((node){0,0});
	while(!q.empty()){
		int u=q.top().u;q.pop();
		for(int i=head[u];i;i=last[i]){
			int v=to[i];
			if(dis[v]>dis[u]+w[i]){
				dis[v]=dis[u]+w[i];
				q.push((node){dis[v],v});
			}
		}
	}
	return;
}
signed main(){
	h=read()-1,x=read(),y=read(),z=read();
	for(int i=0;i<x;i++){
		add(i,(i+y)%x,y);
		add(i,(i+z)%x,z); 
	}
	dijkstra();int ans=0;
	for(int i=0;i<x;i++)if(h>=dis[i])ans+=(h-dis[i])/x+1;
	printf("%lld\n",ans);
	return 0;
} 

P2371 [国家集训队]墨墨的等式

x 看作跳的层数, b 看作最终跳到哪里,那么就与上题一样了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+110;
int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
int n,l,r,a[N];
int dis[N],head[N],to[N*13],last[N*13],w[N*13],tot;
void add(int u,int v,int t){to[++tot]=v,w[tot]=t,last[tot]=head[u],head[u]=tot;return;}
struct node{
	int dis,u;
	bool operator<(const node A)const{return A.dis<dis;}
};
void dijkstra(){
	memset(dis,0x3f,sizeof(dis));dis[0]=0;
	priority_queue<node>q;q.push((node){0,0});
	while(!q.empty()){
		int u=q.top().u;q.pop();
		for(int i=head[u];i;i=last[i]){
			int v=to[i];
			if(dis[v]>dis[u]+w[i]){
				dis[v]=dis[u]+w[i];
				q.push((node){dis[v],v});
			}
		}
	}
	return;
}
signed main(){
	n=read(),l=read(),r=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=0;i<a[1];i++){
		for(int j=2;j<=n;j++)
			add(i,(i+a[j])%a[1],a[j]);
	}
	dijkstra();int ans=0;
	for(int i=0;i<a[1];i++){
		if(r>=dis[i])ans+=(r-dis[i])/a[1]+1;
		if(l-1>=dis[i])ans-=(l-1-dis[i])/a[1]+1;
	}
	printf("%lld\n",ans);
	return 0;
} 

P2662 牛场围栏

还是按 x 的余数分类,求出没类数最小表示长度 len[i], 答案就是 max(len[i]x)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=3010;
int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,bk[N],a[N];
int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}
int dis[N],head[N],to[N*3000],last[N*3000],w[N*3000],tot;
void add(int u,int v,int t){to[++tot]=v,w[tot]=t,last[tot]=head[u],head[u]=tot;return;}
struct node{
	int dis,u;
	bool operator<(const node A)const{return A.dis<dis;}
};
void dijkstra(){
	memset(dis,0x3f,sizeof(dis));dis[0]=0;
	priority_queue<node>q;q.push((node){0,0});
	while(!q.empty()){
		int u=q.top().u;q.pop();
		for(int i=head[u];i;i=last[i]){
			int v=to[i];
			if(dis[v]>dis[u]+w[i]){
				dis[v]=dis[u]+w[i];
				q.push((node){dis[v],v});
			}
		}
	}
	return;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++){
		int t=read();
		for(int i=t;i>=max(1,t-m);i--)bk[i]=1;
	}
	for(int i=1;i<=3000;i++)
		if(bk[i])a[++k]=i;
	int t=a[1];
	for(int i=2;i<=k;i++)t=gcd(t,a[i]);
	if(t!=1 || a[1]==1){
		printf("-1\n");
		return 0;
	}
	for(int u=0;u<a[1];u++)
		for(int i=2;i<=k;i++)
			add(u,(u+a[i])%a[1],a[i]);
	dijkstra();int ans=0;
	for(int i=0;i<a[1];i++){
		ans=max(ans,dis[i]-a[1]);
	}
	printf("%d\n",ans);
	return 0;
}

[ABC077D] Small Multiple

把数按 %K 的余数分类,权值为数位累加和,答案就是 %k=0 的最小表示法。

  1. 如果把个位 +1 ,余数加一(或归零),答案加一,建边 (u,(u+1)%K,1)
  2. 如果乘 10, 答案不变,建边 (u,(10u)%K,0)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+110;
int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
int k;
int dis[N],head[N],to[N*3],last[N*3],w[N*3],tot;
void add(int u,int v,int t){to[++tot]=v,w[tot]=t,last[tot]=head[u],head[u]=tot;return;}
struct node{
	int dis,u;
	bool operator<(const node A)const{return A.dis<dis;}
};
void dijkstra(){
	dis[0]=2e9;
	for(int i=1;i<k;i++){
		int j=i;
		while(j)dis[i]+=j%10,j/=10;		
	}
	priority_queue<node>q;
	for(int i=1;i<k;i++)q.push((node){dis[i],i});
	while(!q.empty()){
		int u=q.top().u;q.pop();
		for(int i=head[u];i;i=last[i]){
			int v=to[i];
			if(dis[v]>dis[u]+w[i]){
				dis[v]=dis[u]+w[i];
				q.push((node){dis[v],v});
			}
		}
	}
	return;
}
int main(){
	k=read();
	for(int i=0;i<k;i++){
		add(i,(i+1)%k,1);
		add(i,(i*10)%k,0);
	}
	dijkstra();
	printf("%d\n",dis[0]);
	return 0;
}
posted @   FJOI  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示