【BZOJ2118】墨墨的等式
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2118
好巧妙的一道题!第一眼看感觉是一道数论题,而且和小凯的疑惑还有些相似之处,其实人家是一道图论题。。。
看别人写的博客都比较涩,我也是看了好多,想了好久才明白,所以,下面我尽量好好写,让人更好理解。
题目大意是问,在一段区间里,有多少个数B可以使a1x1+a2x2+...+anxn=B有解。
我们知道,对于这n个a,k*a+c可以表示所有非负整数,k和c均为非负整数且0<=c<a。所以我们不如让a取n个a中最小的。
假设我们已求得k*a+c可以使上述方程有解,那么显然(k+1)*a+c也可以,所以只要求出最小的k*a+c就可以了。
其实相当于用若干个a1、a2、...、an凑k*a+c,a是不需要凑的,关键是c,我们关心能不能凑出一个最小的数使之%a=c。
当c=0一定是可以的,而且这个数就是0,那么给0加上任意一个a,就可以凑出另一个c了(现在的a!=最小的a),但是需要加上那个a,也就是会有代价,因为我们想要最小的那个数。
我们发现这不就是最短路问题吗?一共有0到a-1共a个点(因为是在%a意义下),每个点向外有些边指向另外的点,这些边还有权值,我们想知道从起点0到其他点过程中经过边最小权值之和。
所以,对于0到a-1每个点i,建一条从i指向(i+a[j])%a,权值为a[j]的边,跑一遍最短路就可以求出%a=c的最小的数。当然a[j]是指上述n个a的每一个,为了避免有自环,最小的a跳过就好了。
求出来以后,我们对他+a进行调整,在要求的范围内的计数即可,不过统计答案这里还是需要处理细节的,如果你选择了一种比较快的方法。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 6 using namespace std; 7 8 typedef long long ll; 9 10 const int maxn = 15, maxp = 5e5 + 5; 11 ll inf = 1e12 + 5; 12 13 int n, a[maxn], p = maxp; 14 15 int head[maxp], eid; 16 17 struct Edge { 18 int v, w, next; 19 } edge[maxp * maxn]; 20 21 inline void insert(int u, int v, int w) { 22 edge[++eid].v = v; 23 edge[eid].w = w; 24 edge[eid].next = head[u]; 25 head[u] = eid; 26 } 27 28 int vis[maxp]; 29 ll dist[maxp]; 30 31 struct node { 32 int id, dist; 33 node(int i, int d) : id(i), dist(d) {} 34 bool operator < (const node& rhs) const { 35 return dist > rhs.dist; 36 } 37 }; 38 39 priority_queue<node> q; 40 41 inline void dijkstra() { 42 memset(dist, inf, sizeof(dist)); 43 dist[0] = 0; 44 q.push(node(0, 0)); 45 while (!q.empty()) { 46 int u = q.top().id; 47 q.pop(); 48 if (vis[u]) continue; 49 vis[u] = 1; 50 for (int p = head[u]; p; p = edge[p].next) { 51 int v = edge[p].v, w = edge[p].w; 52 if (dist[v] > dist[u] + w) { 53 dist[v] = dist[u] + w; 54 q.push(node(v, dist[v])); 55 } 56 } 57 } 58 } 59 60 int main() { 61 ll bmin, bmax, ans = 0; 62 scanf("%d%lld%lld", &n, &bmin, &bmax); 63 for (int i = 1; i <= n; ++i) { 64 scanf("%d", &a[i]); 65 p = min(p, a[i]); 66 } 67 for (int i = 0; i < p; ++i) 68 for (int j = 1; j <= n; ++j) { 69 if (a[j] == p) continue; //避免有自环 70 insert(i, (i + a[j]) % p, a[j]); 71 } 72 dijkstra(); 73 for (int i = 0; i < p; ++i) { 74 if (dist[i] > bmax) continue; 75 ll l = max(0LL, (bmin - dist[i]) / p); 76 ll r = (bmax - dist[i]) / p; 77 if (l * p + dist[i] < bmin) ++l; 78 ans += r - l + 1; 79 } 80 printf("%lld", ans); 81 return 0; 82 }