【图论经典题目讲解】洛谷 P2371 墨墨的等式

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 = 1 n a i x i = b \sum\limits_{i=1}^n a_ix_i=b i=1∑naixi=b 存在非负整数解( x i x_i xi 为变量, a a a 数组给定)。

S o l u t i o n \mathrm{Solution} Solution

b b b 一定可以表示为 q + k v q+kv q+kv 的形式,其中 q ∈ [ 0 , v ) q\in [0,v) q∈[0,v) 表示余数, k ∈ Z k\in \mathbb{Z} k∈Z。

故,思路可以转化为选择合适的 v v v,对于枚举余数 q ∈ 0 ∼ v − 1 q\in 0\sim v - 1 q∈0∼v−1,求解通过 ∑ i = 1 n a i x i = b \sum\limits_{i=1}^n a_ix_i=b i=1∑naixi=b,所能凑出的最小的 b b b,使得 b b b 模 v v v 为 q q q。

那么,为了能够不重不漏的计算出所有的 b b b, v v v 应取 min ⁡ ( a i ) \min(a_i) min(ai),即 a a a 数组中的最小值,这样只要能够由题目中的等式凑出的 b b b,就必定能由 q + k v q+kv q+kv 的形式。(可以感性理解)

下面就是如何能够求解最小的余数为 q q q 出的 b b b( q ∈ [ 0 , v − 1 ] q\in[0,v-1] q∈[0,v−1]),这就运用到了 同余最短路

将余数 i i i 连一条向 ( i + a j )   m o d   v (i+a_j)\bmod v (i+aj)modv 长度为 a j a_j aj 的边,对于每一个 j ∈ [ 1 , n ] j\in [1,n] j∈[1,n]。这表示从 i i i 这个余数加入 a j a_j aj 后,将会变为的一个新的余数。在这个图中求从 0 0 0 号点到所有点的最短路后,对于 i i i 号点的含义就是凑出余数为 i i i 的最小的数。

最后,通过前缀和的思想可以得到 l ∼ r l\sim r l∼r 的 b b b 的个数为 0 ∼ r 0\sim r 0∼r 的 b b b 的个数减去 0 ∼ l − 1 0\sim l-1 0∼l−1 的 b b b 的个数。对于每一个 i i i,设凑数余数为 i i i 的最小的数为 b ′ b' b′,则若当前最高限制为 x x x,则 b b b 的个数为 ⌊ x − b ′ v ⌋ + 1 \lfloor\frac{x-b'}{v}\rfloor+1 ⌊vx−b′⌋+1。

C o d e Code Code

cpp 复制代码
#include <bits/stdc++.h>
#define int long long

using 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;
}
相关推荐
sp_fyf_202422 分钟前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-02
人工智能·神经网络·算法·计算机视觉·语言模型·自然语言处理·数据挖掘
rjszcb34 分钟前
一文说完c++全部基础知识,IO流(二)
c++
小字节,大梦想1 小时前
【C++】二叉搜索树
数据结构·c++
吾名招财1 小时前
yolov5-7.0模型DNN加载函数及参数详解(重要)
c++·人工智能·yolo·dnn
我是哈哈hh2 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
憧憬成为原神糕手2 小时前
c++_ 多态
开发语言·c++
郭二哈2 小时前
C++——模板进阶、继承
java·服务器·c++
Tisfy2 小时前
LeetCode 2187.完成旅途的最少时间:二分查找
算法·leetcode·二分查找·题解·二分
挥剑决浮云 -2 小时前
Linux 之 安装软件、GCC编译器、Linux 操作系统基础
linux·服务器·c语言·c++·经验分享·笔记
Mephisto.java2 小时前
【力扣 | SQL题 | 每日四题】力扣2082, 2084, 2072, 2112, 180
sql·算法·leetcode