P2371 [国家集训队]墨墨的等式

P2371 国家集训队 墨墨的等式

同余最短路,考虑直接用最小的代价拼出来在膜最小 \(a_i\) 意义下的余数。然后不停地累加最小的 \(a_i\)(一下称它 \(a_{min}\)) 就行了。
正确性:假定 \(i,j,k\) 为膜 \(a_{min}\) 下的余数,令 \(i+j \equiv k (\mod a_{min})\) 那么显然 \(i+j = t \cdot a_{min} + k\) (\(t\) 为任意非负整数)
那么在 \(k + t \cdot a_{min}\) 一定可以全覆盖 \((i+j) + t \cdot a_{min}\)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>

using namespace std;

typedef long long ll;
const ll MAXN = 6e6+10;
const ll MAXM = 5e5+10;

struct edge {
    ll nt, to, v;
} E[MAXN];

ll a[MAXM], N, head[MAXM], cnt = -1, vis[MAXM], dis[MAXM], L, R, ans;

void add(ll, ll, ll);
void spfa(ll);

int main() {
    memset(head, -1, sizeof(head));
    scanf("%lld%lld%lld", &N, &L, &R); L--;
    for (ll i = 1; i <= N; i++) scanf("%lld", a+i);
    sort(a+1, a+N+1);
    for (ll i = 0; i < a[1]; i++) for (ll j = 1; j <= N; j++) add(i, (i + a[j]) % a[1], a[j]);
    spfa(0);
    for (ll i = 0; i < a[1]; i++) {
    	if (dis[i] <= L) ans -= ((L - dis[i]) / a[1]) + 1;
    	if (dis[i] <= R) ans += ((R - dis[i]) / a[1]) + 1;
	}
    printf("%lld\n", ans);
    return 0;
}

void spfa(ll st) {
    queue <ll> q;
    q.push(st);
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    vis[st] = 1;
    dis[st] = 0;
    while (!q.empty()) {
        ll nt = q.front();
        q.pop();
        vis[nt] = 0;
        for (ll i = head[nt]; ~i; i = E[i].nt) {
            ll v = E[i].to;
            if (dis[v] > dis[nt] + E[i].v) {
                dis[v] = dis[nt] + E[i].v;
                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
}

void add(ll x, ll y, ll v) {
    cnt++;
    E[cnt].to = y;
    E[cnt].nt = head[x];
    E[cnt].v = v;
    head[x] = cnt;
}

posted @ 2020-09-19 18:20  Gensokyo_Alice  阅读(94)  评论(0编辑  收藏  举报