[圖論]同餘最短路
阿新 • • 發佈:2022-03-18
同餘最短路
介紹
給出若干個數,每個數有無限個.求這些數加起來能組成多少個不同的數
例題切入
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\)
跑一遍最短路即可得出\(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; }