# 同餘最短路
> 題目:給三個數字$x,y,z$,每個數字可以被加任意次,求總和有多少種可能在區間$[1,K]$
假設$k<=K$、$a_i$為任意數,則題目原式為:
$$xa_1+ya_2+za_3=k$$
我們不妨先假設$x<y<z$
初步的概念是枚舉$y$和$z$的數量,這樣會得到一個總和$sum(=ya_2+za_3)$,接著另$rest=K-sum$,代表剩餘的量,這些都會由$x$構成,因此枚舉$y$和$z$組合構成答案的數量等於$x$還能枚舉多少,也就是$\frac{rest}{x}+1$ ($+1$是因為$a_1$=0的時候)。
接下來我們可以把$sum\ mod\ x$,這樣方便於我們可以直接用上述公式計算($sum$超過$x$的幾倍可直接在枚舉$x$的時候補回來),因此我們現在的問題變成計算所有在區間$[0,x-1]$中的$sum$,答案就是$\frac{k-sum_i}{x}+1\ (0 <=i < x)$ 的總和,那為了答案的完整性,對於每一個$mod\ x$後一樣的$sum$,要找最小的,這樣才能枚舉盡可能多的$x$,答案也才會是完整的。
那既然要找最小的$sum$,就可以用最短路徑演算法,我們基於在當前點$i$,加一條邊長度是$y$並且會通到$(i+y)\%x$的邊和一條邊長度是$z$並且會通到$(i+z)\%x$的邊,就會得到一張有向圖:
```
y
i--->(i+y)mod(x)
z
i--->(i+z)mod(x)
```
因為我們只要求$[0,x-1]$的範圍所以要$mod\ x$
最後,答案就是
$$\displaystyle\sum_{i=0} ^{x-1}(\frac{K-sum\ mod\ x=i中的最短路}{x})+1$$
[模板題](https://www.luogu.com.cn/problem/P3403#submit)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define pb push_back
#define pii pair<int, int>
const int MAXN = 100005;
vector<pii> Graph[MAXN];
signed main() {
int x, y, z, k;
cin >> k >> x >> y >> z;
k--;
if (x > y) swap(x, y);
if (x > z) swap(x, z);
if (y > z) swap(y, z);
for (int i = 0; i < x; i++) {
Graph[i].pb({(i + y) % x, y});
Graph[i].pb({(i + z) % x, z});
}
int start = 0;
vector<int> dis(x, LLONG_MAX);
dis[start] = 0;
priority_queue<pii, vector<pii>, greater<pii>> pq;
pq.push({dis[start], start});
while (!pq.empty()) {
pii cur = pq.top();
pq.pop();
int d = cur.first, u = cur.second;
if (dis[u] < d) continue;
for (pii nxt : Graph[u]) {
int v = nxt.first, w = nxt.second;
if (dis[u] + w < dis[v]) {
dis[v] = dis[u] + w;
pq.push({dis[v], v});
}
}
}
int ans = 0;
for (int i = 0; i < x; i++) {
if (dis[i] > k) continue;
ans += (k - dis[i]) / x + 1;
}
cout << ans << ' ';
}
```