[算法学习] 同余最短路
对于一些形如询问$$\sum_{i = 1} ^ n a_i \times p_i = k$$的整数解或者有解的情况时,常常使用同余最短路的解法。
我们令\(dis_i\)记录凑出\(\bmod base\)余i最小的数是多少
然后可以用\(\text{dijkstra}\)的方法转移
\(f((x + y) \bmod base) = f(x) + y\)
const int N = 12 + 5;
const int M = 5e5 + 7;
int n, E, a[N], head[M];
ll l, r, ans;
struct EDGE {
int w, to, nxt;
} edge[M * 12];
inline void addedge(int u, int v, int w) {
edge[++E].to = v;
edge[E].w = w;
edge[E].nxt = head[u];
head[u] = E;
}
namespace DIJKSTRA {
ll dis[M];
struct Node {
ll u, d;
Node(int U = 0, ll D = 0) {
u = U; d = D;
}
bool operator < (const Node&rhs) const {
return d > rhs.d;
}
};
priority_queue < Node > q;
inline void dijkstra() {
for(int i = 0; i < a[0]; i++) dis[i] = INF;
while(q.size()) q.pop();
q.push(Node(0, 0)); dis[0] = 0;
while(!q.empty()) {
Node t = q.top(); q.pop();
ll u = t.u, d = t.d;
if(d != dis[u]) continue;
for(int i = head[u]; i; i = edge[i].nxt) {
ll v = edge[i].to, w = edge[i].w;
if(dis[u] != INF && dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push(Node(v, dis[v]));
}
}
}
}
}
int main() {
read(n); read(l); read(r);
for(int i = 0; i < n; i++) read(a[i]);
sort(a, a + n);
for(int i = 0; i < a[0]; i++) {
for(int j = 1; j < n; j++) {
addedge(i, (i + a[j]) % a[0], a[j]);
}
}
DIJKSTRA::dijkstra();
for(int i = 0; i < a[0]; i++) {
ll lans = 0, rans = 0;
if(l - 1 >= DIJKSTRA::dis[i]) lans = (l - 1 - DIJKSTRA::dis[i]) / a[0] + 1;
if(r >= DIJKSTRA::dis[i]) rans = (r - DIJKSTRA::dis[i]) / a[0] + 1;
ans += rans - lans;
}
printf("%lld\n", ans);
return 0;
}