P2371 墨墨的等式
D e s c r i p t i o n\mathrm{Description}Description
求解有多少个 b ∈ [ l , r ]b\in [l,r]b∈[l,r] 满足 ∑ i = 1naixi= b\sum\limits_{i=1}^n a_ix_i=bi=1∑naixi=b 存在非负整数解( xi x_ixi 为变量, aaa 数组给定)。
S o l u t i o n\mathrm{Solution}Solution
bbb 一定可以表示为 q + k vq+kvq+kv 的形式,其中 q ∈ [ 0 , v )q\in [0,v)q∈[0,v) 表示余数, k ∈ Zk\in \mathbb{Z}k∈Z。
故,思路可以转化为选择合适的 vvv,对于枚举余数 q ∈ 0 ∼ v − 1q\in 0\sim v – 1q∈0∼v−1,求解通过 ∑ i = 1naixi= b\sum\limits_{i=1}^n a_ix_i=bi=1∑naixi=b,所能凑出的最小的 bbb,使得 bbb 模 vvv 为 qqq。
那么,为了能够不重不漏的计算出所有的 bbb, vvv 应取 min ( ai)\min(a_i)min(ai),即 aaa 数组中的最小值,这样只要能够由题目中的等式凑出的 bbb,就必定能由 q + k vq+kvq+kv 的形式。(可以感性理解)
下面就是如何能够求解最小的余数为 qqq 出的 bbb( q ∈ [ 0 , v − 1 ]q\in[0,v-1]q∈[0,v−1]),这就运用到了 同余最短路。
将余数 iii 连一条向 ( i + aj) m o d v(i+a_j)\bmod v(i+aj)modv 长度为 aj a_jaj 的边,对于每一个 j ∈ [ 1 , n ]j\in [1,n]j∈[1,n]。这表示从 iii 这个余数加入 aj a_jaj 后,将会变为的一个新的余数。在这个图中求从 000 号点到所有点的最短路后,对于 iii 号点的含义就是凑出余数为 iii 的最小的数。
最后,通过前缀和的思想可以得到 l ∼ rl\sim rl∼r 的 bbb 的个数为 0 ∼ r0\sim r0∼r 的 bbb 的个数减去 0 ∼ l − 10\sim l-10∼l−1 的 bbb 的个数。对于每一个 iii,设凑数余数为 iii 的最小的数为 b′ b’b′,则若当前最高限制为 xxx,则 bbb 的个数为 ⌊x − b′ v⌋ + 1\lfloor\frac{x-b’}{v}\rfloor+1⌊vx−b′⌋+1。
C o d eCodeCode
#include #define int long longusing namespace std;typedef pair<int, int> PII;typedef long long LL;const int SIZE = 5e6 + 10;int N, L, R;int A[20];int h[SIZE], e[SIZE], ne[SIZE], w[SIZE], idx;int Dist[SIZE], Vis[SIZE];void add(int a, int b, int c){e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;}void Dijkstra(int Start){memset(Dist, 0x3f, sizeof Dist);memset(Vis, 0, sizeof Vis);priority_queue<PII, vector<PII>, greater<PII>> Heap;Heap.push({0, Start}), Dist[Start] = 0;while (Heap.size()){auto Tmp = Heap.top();Heap.pop();int u = Tmp.second;if (Vis[u]) continue;Vis[u] = 1;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (Dist[j] > Dist[u] + w[i]){Dist[j] = Dist[u] + w[i];Heap.push({Dist[j], j});}}}}signed main(){cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);memset(h, -1, sizeof h);cin >> N >> L >> R;int Min = 1e18;for (int i = 1; i <= N; i ++){cin >> A[i];if (A[i]) Min = min(Min, A[i]);}for (int i = 0; i < Min; i ++)for (int j = 1; j <= N; j ++)if (A[j] != Min && A[j])add(i, (i + A[j]) % Min, A[j]);Dijkstra(0);int Tot1 = 0, Tot2 = 0;for (int i = 0; i < Min; i ++)if (Dist[i] <= R)Tot1 += (R - Dist[i]) / Min + 1;for (int i = 0; i < Min; i ++)if (Dist[i] < L)Tot2 += (L - 1 - Dist[i]) / Min + 1;cout << Tot1 - Tot2 << endl;return 0;}