1. 程式人生 > 其它 >[圖論]同餘最短路

[圖論]同餘最短路

同餘最短路

介紹

給出若干個數,每個數有無限個.求這些數加起來能組成多少個不同的數


例題切入

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\)

我們觀察上式,會發現其形式與最短路\([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;
}

參考部落格