Description
大致就是给定等式(a_{1}x_{1} + a_{2}x_{2} + a_{3}x_{3} dots + a_{n}x_{n}= B), 现在给定({a}),(B)的取值范围([B_{Min}, B_{Max}]), 求B的取值范围内有多少个B使({x})有非负整数解.
Solution
先进行一个简单的转化, 直接求([0, l - 1]),([0,r]) 的解然后减去.就只要考虑怎么求([0, a])的答案.
我们进行一个简单的转化, 现在给定n个物品, 每个物品有无数个,求能表示出的价值的个数.
考虑一个数Z作为价值, 如果(l)能被Z表示为(l = kZ + D), 那么((l + z))也能被表示为((k + 1)Z + D)
可以简单的发现(D leq Z) 于是我们考虑答案按照模(k)的剩余系分类, 然后最后计算剩余系的总和.
设(a_i)为$a_i = Min{Q}, Qmodk = i $且能被构造.那么答案就很好计算.
我们考虑(k)怎么取值.显然,k取(min {a_i})最好, 因为它的剩余系大小最小.
那么对于任意一个i和a[j], 我们用最短路求解, 从(i)向((a_j + i) \% k),连接一条长度为(a_{j})的边.
然后跑一遍最短路即可
Codes
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(s) debug("The massage in line %d, Function %s: %s
", __LINE__, __FUNCTION__, s)
typedef long long LL;
typedef long double LD;
const int BUF_SIZE = (int)1e6 + 10;
struct fastIO {
char buf[BUF_SIZE], buf1[BUF_SIZE];
int cur, cur1;
FILE *in, *out;
fastIO() {
cur = BUF_SIZE, in = stdin, out = stdout;
cur1 = 0;
}
inline char getchar() {
if(cur == BUF_SIZE) fread(buf, BUF_SIZE, 1, in), cur = 0;
return *(buf + (cur++));
}
inline void putchar(char ch) {
*(buf1 + (cur1++)) = ch;
if (cur1 == BUF_SIZE) fwrite(buf1, BUF_SIZE, 1, out), cur1 = 0;
}
inline void flush() {
if (cur1 > 0) fwrite(buf1, cur1, 1, out);
cur1 = 0;
}
}IO;
LL read() {
char ch = IO.getchar();
LL x = 0, flag = 1;
for(;!isdigit(ch); ch = IO.getchar()) if(ch == '-') flag *= -1;
for(;isdigit(ch); ch = IO.getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(LL x) {
if(x < 0) x = -x, IO.putchar('-');
if(x >= 10) write(x / 10);
IO.putchar(x % 10 + 48);
}
#define Maxn 100
#define Maxc 1000009
LL n, l, r, a[Maxn];
struct edge {
int to, nxt, w;
}g[Maxc * 20];
int head[Maxc], e;
LL Min = LLONG_MAX;
void add(int u, int v, int w) {
g[++e] = (edge){v, head[u], w}, head[u] = e;
}
namespace SSSP {
struct node {
LL id, d;
int operator < (const node b) const {
return d > b.d;
}
};
LL dis[Maxc]; int vis[Maxc];
priority_queue <node> que;
void Init_Graph() {
clar(head, -1);
rep(i, 0, Min - 1)
rep(j, 1, n) add(i, (1ll * i + a[j]) % Min, a[j]);
}
void Dijkstra() {
clar(dis, 0x3f);
que.push((node){0, 0}); dis[0] = 0;
while(!que.empty()) {
node u = que.top(); que.pop();
if(vis[u.id]) continue;
vis[u.id] = 1;
for(int i = *(head + u.id); ~i; i = g[i].nxt) {
int v = g[i].to;
if(!vis[v] && dis[v] > dis[u.id] + g[i].w) {
dis[v] = dis[u.id] + g[i].w;
que.push({v, dis[v]});
}
}
}
}
}
LL solve(LL a) {
LL ret = 0;
rep(i, 0, Min - 1)
if(SSSP :: dis[i] <= a)
ret += (a - SSSP :: dis[i]) / Min + 1;
return ret;
}
int main() {
#ifdef Qrsikno
freopen("BZOJ2118.in", "r", stdin);
freopen("BZOJ2118.out", "w", stdout);
#endif
n = read(), l = read(), r = read();
rep(i, 1, n) a[i] = read(), Min = min(a[i], Min);
SSSP :: Init_Graph();
SSSP :: Dijkstra();
write(solve(r) - solve(l - 1));
IO.flush();
return 0;
}
启发
对于一种求最小化(a_i)的值的问题,并且(a_i)可以由其他项目简单的动态的求出.可以考虑最短路求解.这事实上提供了一种建模的好方法.
如果(a_i) 的取值范围过于大, 可以考虑按模数分类后计算, 统计答案时求和