同余最短路
同余最短路
一堆点凑成了 $\mathrm{sum}$, 则 $\mathrm{sum}$ 可以表示成 $\mathrm{k \times a[1] + i}$ 的形式.
即 $\mathrm{k}$ 为商,后面的 $\mathrm{i}$ 为 $\mathrm{mod}$ $\mathrm{a[1]}$ 的余数.
令 $\mathrm{dis[i]}$ 表示 $\mathrm{sum}$ 模 $\mathrm{a[1]}$ 等于 $\mathrm{i}$ 的情况下 $\mathrm{sum}$ 的最小值.
通过 $\mathrm{dis[i]}$ 可以得到商,也就是 $\mathrm{k}$ 的最小值.
通过 $\mathrm{k_{min}}$,可得到所有 $\mathrm{k' \times a[1]+i}$, $\mathrm{k_{min} \leqslant k'}$.
由于后面的余数 $\mathrm{i}$ 不同,所以不重不漏统计到 $\mathrm{sum}$ 的所有可能取值.
墨墨的等式
来源:洛谷P2371 [国家集训队]墨墨的等式
同余最短路的模板题,统计 $[l,r]$ 区间内的答案时直接 $\mathrm{calc(r)-calc(l-1)}$ 即可.
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 500009
#define ll long long
#define pb push_back
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
const ll inf=(ll)1e12;
queue<int>q;
int a[12],inq[N];
ll dis[N];
int main() {
// setIO("input");
int n ;
ll L, R;
scanf("%d%lld%lld",&n,&L,&R);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
sort(a+1,a+1+n);
int mx=a[1];
for(int i=0;i<mx;++i) dis[i]=inf;
dis[0]=0;
inq[0]=1;
q.push(0);
while(!q.empty()) {
int u=q.front(); q.pop();
inq[u]=0;
for(int i=1;i<=n;++i) {
int v = (u + a[i]) % mx;
if(dis[v] > dis[u] + a[i]) {
dis[v] = dis[u] + a[i];
if(!inq[v]) {
q.push(v);
inq[v] = 1;
}
}
}
}
for(int i=0;i<mx;++i) {
if(dis[i] == inf) continue;
dis[i] -= i;
dis[i] /= mx;
}
ll ans=0;
for(int i=0;i<mx;++i) {
ll pl = (L - 1 - i) / mx;
ll pr = (R - i) / mx;
ll t1 = max(0ll, pl - dis[i] + 1);
ll t2 = max(0ll, pr - dis[i] + 1);
ans += t2 - t1;
}
printf("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号