BZOJ 2118 - 墨墨的等式
Published on 2016-11-07描述
给定 、、 的范围 ,求有多少个 可以使得方程:
存在非负整数解集。
分析
对于 存在非负整数解,换句话说,就是用任意多个 ,可以凑出 来。
设 ,则我们可以知道,如果 能够凑出来,那么 也能被凑出来。
如果 ,那么 可以凑出所有不小于 且对 取模得 的数。
显然对于每一个 ,我们只需要考虑最小的能被凑出的 ,满足 。我们设对于 来说这个最小的 为 。
回到这个题目,不妨设 为 内的答案,那么求 内的答案可以转化为 。
那么我们现在考虑求 中的答案:考虑从每一个 开始扩展, 可以扩展到所有小于等于 且不小于 且对 取模得 的数,所以
我们来证明这样的扩展方式能够不重不漏的凑出 中所有能被凑出的数。
不重:如果一个数 能被凑出来,不妨设 ,因为每一个 只可能更新模 余 的数,所以 只会被 更新,所以没有重复。
不漏:根据我们的计算式, 会扩展到所有小于等于 且不小于 且对 取模得 的数。如果 能被凑出来,但是我们没计算到它,不妨设 ,我们一定可以得到 ,这与 的定义矛盾。所以不会遗漏。
问题变成了求 ,这是一个经典问题,考虑使用最短路求出,建立 个点,点 连出边 。求出从 出发到每个点的最短路 ,即得 。
代码
// Created by Sengxian on 2016/11/6. // Copyright (c) 2016年 Sengxian. All rights reserved. // BZOJ 2118 数论 + 最短路 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAX_N = 12, MAX_NODE = 500000 + 3; const ll INF = 0x3f3f3f3f3f3f3f3fLL; typedef pair<ll, int> state; int n, a[MAX_N], mn; ll L, R; inline bool tension(ll &a, const ll &b) { return b < a ? a = b, true : false; } ll dis[MAX_NODE]; void Dijkstra() { priority_queue<state, vector<state>, greater<state> > pq; memset(dis, 0x3f, sizeof dis); dis[0] = 0; pq.push(state(0, 0)); while (!pq.empty()) { state now = pq.top(); pq.pop(); int u = now.second; if (now.first > dis[u]) continue; for (int i = 0; i < n; ++i) if (tension(dis[(u + a[i]) % mn], dis[u] + a[i])) pq.push(state(dis[(u + a[i]) % mn], (u + a[i]) % mn)); } } inline ll F(const ll m) { ll ans = 0; for (int i = 0; i < mn; ++i) if (dis[i] <= m) ans += (m - dis[i]) / mn + 1; return ans; } int main() { scanf("%d%lld%lld", &n, &L, &R); for (int i = 0; i < n; ++i) { scanf("%d", a + i); if (a[i] == 0) n--, i--; } mn = *min_element(a, a + n); Dijkstra(); printf("%lld\n", F(R) - F(L - 1)); return 0; }