同余最短路
同余最短路
介绍
给出若干个数,每个数有无限个.求这些数加起来能组成多少个不同的数
例题切入
Luogu 3403 跳楼机
本质上是要求\(ax+by+cz=K\) {\(K \in [1,h]\)}
考虑f[i]等于在\(mod x\)意义下使用\(y,z\)能够到达i的最小楼层
为了使i的范围尽可能的小,我们不妨令x为三个数之中最小的那个
那么很显然可以得出以下式子:
- \(f[(i+y)\;mod\;x]=f[i]+y\)
- \(f[(i+z)\;mod\;x]=f[i]+z\)
我们观察上式,会发现其形式与最短路\(f[y]=f[x]+z\)十分相似.
这便是所谓的同余最短路.
在\((i+y)\;mod\;x\)和\(i\)点之间连一边权为\(y\)的边.对于\(z\)同理.
跑一遍最短路即可得出\(f\)数组.
最后\(ans=\Sigma \lfloor \frac{h-f[i]}{x} \rfloor+1\)
- 根据\(f\)数组的定义,可以知道所有可以被\(x,y,z\)表示出来的数都可以表示为\(f[i]+k*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;
}

浙公网安备 33010602011771号