同余最短路
同余最短路#
介绍#
给出若干个数,每个数有无限个.求这些数加起来能组成多少个不同的数
例题切入#
Luogu 3403 跳楼机
本质上是要求 {}
考虑f[i]等于在意义下使用能够到达i的最小楼层
为了使i的范围尽可能的小,我们不妨令x为三个数之中最小的那个
那么很显然可以得出以下式子:
我们观察上式,会发现其形式与最短路十分相似.
这便是所谓的同余最短路.
在和点之间连一边权为的边.对于同理.
跑一遍最短路即可得出数组.
最后
- 根据数组的定义,可以知道所有可以被表示出来的数都可以表示为,从而也说明了不需要考虑重复计算的问题.
Code:
#include <bits/stdc++.h>
#define N 100010
#define M 100000000
#define ll long long
#define mpr make_pair
#define pr pair<ll,int>
using namespace std;
int x,y,z;
ll h,ans,f[N];
int head[N],edge[M],ver[M],nxt[M],tot;
bool v[N];
priority_queue < pr > q;
void add(int u,int v,int w)
{ver[++tot]=v,edge[tot]=w,nxt[tot]=head[u],head[u]=tot;}
void dijkstra()
{
memset(f,0x3f,sizeof(f));
memset(v,0,sizeof(v));
f[1]=1ll;
q.push(mpr(-1ll,1));
while (q.size())
{
int x=q.top().second;q.pop();
if (v[x]) continue;
v[x]=1;
for (int i=head[x];i;i=nxt[i])
{
int w=ver[i];
if (f[w]>f[x]+edge[i])
{
f[w]=f[x]+edge[i];
q.push(mpr(-f[w],w));
}
}
}
}
int main()
{
scanf("%lld %d %d %d",&h,&x,&y,&z);
if (x==1 || y==1 || z==1)
{
printf("%lld\n",h);
return 0;
}
for (int i=0;i<x;i++)
{
add(i,(i+y)%x,y);
add(i,(i+z)%x,z);
}
dijkstra();
for (int i=0;i<x;i++)
if (f[i]<=h) ans+=(ll)(h-f[i])/x+1;
printf("%lld",ans);
//system("pause");
return 0;
}
Luogu 2371 [国家集训队]墨墨的等式
这一题即上面一题的扩展.
将3个数扩展到了多个数.
本质上没有任何变化.
#include <bits/stdc++.h>
#define N 500010
#define M 100000000
#define ll long long
#define mpr make_pair
#define pr pair<ll,int>
using namespace std;
int n,a[15],mi=500010;
ll l,r,ans,f[N];
int head[N],edge[M],ver[M],nxt[M],tot;
bool v[N];
priority_queue < pr > q;
void add(int u,int v,int w)
{ver[++tot]=v,edge[tot]=w,nxt[tot]=head[u],head[u]=tot;}
void dijkstra()
{
memset(f,0x3f,sizeof(f));
memset(v,0,sizeof(v));
f[0]=(ll)0;
q.push(mpr((ll)0,0));
while (q.size())
{
int x=q.top().second;q.pop();
if (v[x]) continue;
v[x]=1;
for (int i=head[x];i;i=nxt[i])
{
int w=ver[i];
if (f[w]>f[x]+(ll)edge[i])
{
f[w]=f[x]+(ll)edge[i];
q.push(mpr(-f[w],w));
}
}
}
}
ll query(ll x)
{
ll ans=0;
for (int i=0;i<mi;i++)
if (f[i]<=x) ans+=(ll)(x-f[i])/mi+1;
return ans;
}
signed main()
{
scanf("%d %lld %lld",&n,&l,&r);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (a[i]==1)
{
printf("%lld",r-l+1);
return 0;
}
mi=min(mi,a[i]);
}
for (int i=1;i<=n;i++)
{
if (a[i]==mi) continue;
for (int j=0;j<mi;j++)
add(j,(j+a[i])%mi,a[i]);
}
dijkstra();
printf("%lld",query(r)-query(l-1));
//system("pause");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话