luogu 2371墨墨的等式

数论转图论神仙题。

题意可以理解为,有n个物品,第i个物品价值为a[i],每个物品能使用任意次,求在区间[B_min, B_max]中能凑出多少中价值。其中n <= 12, 0 <= a[i] <= 5e5, 1 <= B_min <= B_max <= 1e12

下面假设a都不为0。
以a[0]为例,如果k a[0] + b可以被凑出,那么(k+1)a[0]+b也可以被凑出。所以为了求出所有能被凑出的数,我们要计算对于每个b,k最小能为多少。
在模a[0]意义下,b在加上一个物品a[i]后,变成(b + a[i]) % a[0],然后实际的总价值增加a[i]。所以我们可以这样构建一个图:
对于每个0到a[0]-1的b,以及每个0到n-1的i,连一条从b到(b + a[i]) % a[0],边权为a[i]的有向边。
这样,从0转移到b有多条路径,每条路径的权值即为实际凑出的价值。
而我们要求转移到b的最小价值是多少。
这就可以用最短路做。
求出最小价值之后,后面的价值都能被凑出来。所以对于每个b统计一下答案,然后加起来就好了。

代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;

#define DEBUG 0

#define MAXN 15
#define MAXA 500011

typedef long long LL;

typedef pair<int, int> pii;

struct GRAPH {
    vector<vector<pii> > s;

    void ClearEdges() {
        for (auto& i : s) {
            i.resize(0);
        }
    }
    void Init(int n) {
        ClearEdges();
        s.resize(n+1);
    }
    void AddDi(int u, int v, int w) {
        s[u].emplace_back(make_pair(v, w));
    }
};

template <typename T>
void dijkstra(const GRAPH& g, int s, T dist[]) {
    priority_queue<pair<T, int> >q;

    fill(dist, dist + g.s.size(), numeric_limits<T>::max());
    dist[s] = 0;
    q.push(make_pair(0, s));
    while (!q.empty()) {
        s = q.top().second;
        T c = -q.top().first;
        q.pop();
        if (c > dist[s]) continue;//old version
        for (auto i : g.s[s]) {
            int v = i.first;
            c = i.second;
            if (dist[v] > dist[s] + c) {
                dist[v] = dist[s] + c;
                q.push(make_pair(-dist[v], v));
            }
        }
    }
}

int main() {
    static int a[MAXN];
    static LL dist[MAXA];

    int t;
    LL b0, b1;
    cin >> t >> b0 >> b1;
    --b0;
    int n = 0;
    while (t--) {
        int ta;
        cin >> ta;
        if (ta) {
            a[n++] = ta;
        }
    }
    if (n) {
        int mn = a[0];
        for (int i = 1; i < n; ++i) {
            if (a[i]) {
                mn = min(mn, a[i]);
            }
        }
        GRAPH g;
        g.Init(mn);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < mn; ++j) {
                g.AddDi(j, (j + a[i]) % mn, a[i]);
            }
        }
        dijkstra(g, 0, dist);
#if DEBUG
        for (int i = 0; i < mn; ++i) {
            cout << dist[i] << ' ';
        }
        cout << endl;
#endif
        LL ans = 0;
        for (int i = 0; i < mn; ++i) {
            LL up = b1 / mn + 1;    //The included numbers
            if (b1 % mn < i)
                --up;
            LL down = b0 / mn + 1;
            if (b0 % mn < i)
                --down;
            down = max(down, dist[i] / mn);
            if (up >= down) {
                ans += up - down;
            }
        }
        cout << ans << endl;
    } else {
        cout << "0\n";
    }
    return 0;
}
posted @ 2024-09-28 14:12  寻找繁星  阅读(0)  评论(0编辑  收藏  举报