【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 }
AC代码

 

posted @ 2018-11-03 21:45  Mr^Kevin  阅读(213)  评论(0编辑  收藏  举报