bzoj 2118 墨墨的等式 dijkstra+数论
题面
解法
和今天训练的T3简直一样
建出\(0\)到\(a_1-1\)这\(a_1\)个点,\(dis_i\)表示\(a_1x_1+…a_nx_n\)在\(x_i\)均为非负数的情况下对\(a_1\)取模为\(i\)的数最小是多少
那么,对于每一个\(a_i\),我们可以连接\(j\rightarrow (j+a_i)\%a_1\),边权为\(a_i\)
然后跑出最短路
那么,对于每一种余数,我们都可以知道它在\(B\)取值范围内的那一段区间
然后计算一下区间内对某一个数取模结果为某一个数的数的个数即可
令\(m=min\{a_i\}\)
时间复杂度:\(O(m\ log\ m)\)
代码
#include <bits/stdc++.h>
#define LL long long
#define PI pair <LL, int>
#define mp make_pair
#define N 500010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
int next, num, v;
} e[N * 24];
int cnt, a[N], used[N];
LL dis[N];
void add(int x, int y, int v) {
e[++cnt] = (Edge) {e[x].next, y, v};
e[x].next = cnt;
}
void dijkstra(int s, int n) {
for (int i = 1; i <= n; i++) dis[i] = 1ll << 50;
priority_queue <PI, vector <PI>, greater <PI> > h;
dis[s] = 0, h.push(mp(0, s));
while (!h.empty()) {
PI tmp = h.top(); h.pop();
int x = tmp.second;
if (used[x]) continue; used[x] = 1;
for (int p = e[x].next; p; p = e[p].next) {
int k = e[p].num, v = e[p].v;
if (dis[k] > dis[x] + v)
dis[k] = dis[x] + v, h.push(mp(dis[k], k));
}
}
}
LL calc(LL x, int y, int z) {
LL ret = x / y;
if (x % y >= z && x) ret++;
return ret;
}
int main() {
int n, mn = INT_MAX, mni; LL l, r;
read(n), read(l), read(r);
for (int i = 1; i <= n; i++) {
read(a[i]);
if (mn > a[i]) mn = a[i], mni = i;
}
cnt = mn;
for (int i = 1; i <= n; i++) {
if (i == mni) continue;
for (int j = 0; j < mn; j++)
add(j, (j + a[i]) % mn, a[i]);
}
dijkstra(0, mn - 1); LL ans = 0;
for (int i = 0; i < mn; i++) {
LL tl = max(dis[i], l), tr = r;
if (tl > tr) continue;
ans += calc(tr, mn, i) - calc(tl - 1, mn, i);
}
cout << ans << "\n";
return 0;
}