[DP-1]【转载】同余最短路的转圈技巧
鸣谢:魏老师 提供的神仙思路!
考虑暴力。记
发现题目中
如果
f_i = 1 ,那么\forall j\in a,\ f_{i+j} = 1 。
所以我们并不需要求出所有的
我们从
考虑转移。
暴力的转移即为
注意到对于一个状态
因此,对于每个
计算出
时间复杂度
实现时注意特判
constexpr ll MAXN = 5e5 + 5;
ll n, l, r, f[MAXN];
vector<int> a;
il void solver_main() {
read(n, l, r);
For(i, 1, n) {
int x;
read(x);
if (x)
a.emplace_back(x);
}
if (a.empty())
return puts("0"), void();
sort(a.begin(), a.end());
int M = a[0], len = a.size(); // 选取最小值做 M,减小常数
fill(f, f + M, 1e18 + 5);
f[0] = 0;
For(i, 1, len - 1) {
int lim = __gcd(M, a[i]) - 1; // 环的数量
For(j, 0, lim) {
for (int cur = j, cnt = 0; cnt < 2; cnt += cur == j) { // 转两圈
int nxt = (cur + a[i]) % M;
f[nxt] = min(f[nxt], f[cur] + a[i]), cur = nxt;
}
}
}
ll ans = 0;
For(i, 0, M - 1) {
if (r >= f[i])
ans += (r - f[i]) / M + 1;
if (l > f[i])
ans -= (l - f[i] - 1) / M + 1;
}
cout << ans << endl;
}