定義
-
同餘最短路常用於解決這樣一類問題:
有個正整數,設:
即 使用無限個進行拼湊,然後對的可能值進行各種詢問(例如詢問在區間內的可能值個數,詢問最大的無法拼湊的值,詢問某個定值能否被拼湊出……)
算法思路
定義
取其中一個正整數作爲剩餘系,例如取,設 ,其中,爲即 可拼湊出的 使得 的 最小的 。
建圖
首先根據定義易知: 且 ,可以發現這和最短路的求法相同,
故可建圖,其中,
然後從點開始,求一遍最短路,到點的最短路距離即爲
結果
求得所有的後,
- 所有可拼湊出的值的集合即爲;
- 最大的無法拼湊出的值爲,若爲則表示所有值均可拼湊出來;
- 對於一個定值,若需要知道其具體的拼湊方案,則還需記錄最短路的具體路徑。
時間複雜度
若個正整數有重複值,則最好要去重,且應當取最小的作爲剩餘系,這樣圖更小,求最短路的時間複雜度也更優。
結點數,邊數,利用堆優化Dijkstra算法求最短路時間複雜度爲。
例題
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PLI;
const int INF = 0x3f3f3f3f;
const int LINF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 5e5 + 10;
int n, a[maxn];
LL l, r, dist[maxn];
struct edge
{
int v;
int w;
};
vector<edge> g[maxn];
bool vis[maxn];
void Dijkstra(int s)
{
memset(dist, 0x3f, sizeof(dist));
priority_queue<PLI, vector<PLI>, greater<PLI> > q;
dist[s] = 0;
q.push(make_pair(dist[s], s));
while(!q.empty())
{
int u = q.top().second;
q.pop();
if(vis[u])
continue;
else
vis[u] = true;
for(auto e : g[u])
{
int v = e.v, w = e.w;
if(!vis[v] && dist[u] + w < dist[v])
{
dist[v] = dist[u] + w;
q.push(make_pair(dist[v], v));
}
}
}
}
int main()
{
scanf("%d %lld %lld", &n, &l, &r);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
n = unique(a + 1, a + n + 1) - (a + 1);
for(int i = 0; i < a[1]; i++)
for(int j = 2; j <= n; j++)
g[i].push_back(edge{(i + a[j]) % a[1], a[j]});
Dijkstra(0);
LL ans = 0;
for(int i = 0; i < a[1]; i++)
{
if(r >= dist[i])
ans += (r- dist[i]) / a[1] + 1;
if(l - 1 >= dist[i])
ans -= ((l - 1) - dist[i]) / a[1] + 1;
}
printf("%lld\n", ans);
return 0;
}