同余最短路

同余最短路#

介绍#

给出若干个数,每个数有无限个.求这些数加起来能组成多少个不同的数


例题切入#

Luogu 3403 跳楼机

本质上是要求ax+by+cz=K {K[1,h]}

考虑f[i]等于在modx意义下使用y,z能够到达i的最小楼层

为了使i的范围尽可能的小,我们不妨令x为三个数之中最小的那个

那么很显然可以得出以下式子:

  • f[(i+y)modx]=f[i]+y
  • f[(i+z)modx]=f[i]+z

我们观察上式,会发现其形式与最短路f[y]=f[x]+z十分相似.

这便是所谓的同余最短路.

(i+y)modxi点之间连一边权为y的边.对于z同理.

跑一遍最短路即可得出f数组.

最后ans=Σhf[i]x+1

  • 根据f数组的定义,可以知道所有可以被x,y,z表示出来的数都可以表示为f[i]+kx,从而也说明了不需要考虑重复计算的问题.

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;
}

参考博客

posted @   xkjie  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示
主题色彩