数论转图论神仙题。
题意可以理解为,有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<pii> > s;
vector
void ClearEdges() {
for (auto& i : s) {
.resize(0);
i}
}
void Init(int n) {
();
ClearEdges.resize(n+1);
s}
void AddDi(int u, int v, int w) {
[u].emplace_back(make_pair(v, w));
s}
};
template <typename T>
void dijkstra(const GRAPH& g, int s, T dist[]) {
<pair<T, int> >q;
priority_queue
(dist, dist + g.s.size(), numeric_limits<T>::max());
fill[s] = 0;
dist.push(make_pair(0, s));
qwhile (!q.empty()) {
= q.top().second;
s = -q.top().first;
T c .pop();
qif (c > dist[s]) continue;//old version
for (auto i : g.s[s]) {
int v = i.first;
= i.second;
c if (dist[v] > dist[s] + c) {
[v] = dist[s] + c;
dist.push(make_pair(-dist[v], v));
q}
}
}
}
int main() {
static int a[MAXN];
static LL dist[MAXA];
int t;
, b1;
LL b0>> t >> b0 >> b1;
cin --b0;
int n = 0;
while (t--) {
int ta;
>> ta;
cin if (ta) {
[n++] = ta;
a}
}
if (n) {
int mn = a[0];
for (int i = 1; i < n; ++i) {
if (a[i]) {
= min(mn, a[i]);
mn }
}
;
GRAPH g.Init(mn);
gfor (int i = 0; i < n; ++i) {
for (int j = 0; j < mn; ++j) {
.AddDi(j, (j + a[i]) % mn, a[i]);
g}
}
(g, 0, dist);
dijkstra#if DEBUG
for (int i = 0; i < mn; ++i) {
<< dist[i] << ' ';
cout }
<< endl;
cout #endif
= 0;
LL ans for (int i = 0; i < mn; ++i) {
= b1 / mn + 1; //The included numbers
LL up if (b1 % mn < i)
--up;
= b0 / mn + 1;
LL down if (b0 % mn < i)
--down;
= max(down, dist[i] / mn);
down if (up >= down) {
+= up - down;
ans }
}
<< ans << endl;
cout } else {
<< "0\n";
cout }
return 0;
}