【同余最短路】P2371 [国家集训队] 墨墨的等式|省选-
本文涉及知识点
C++图论 最短路
P2371 [国家集训队] 墨墨的等式
题目描述
墨墨突然对等式很感兴趣,他正在研究 ∑i=1naixi=b\sum_{i=1}^n a_ix_i=b∑i=1naixi=b 存在非负整数解的条件,他要求你编写一个程序,给定 n,a1…n,l,rn, a_{1\dots n}, l, rn,a1…n,l,r,求出有多少 b∈[l,r]b\in[l,r]b∈[l,r] 可以使等式存在非负整数解。
输入格式
第一行三个整数 n,l,rn,l,rn,l,r。
第二行 nnn 个整数 a1…na_{1\dots n}a1…n。
输出格式
一行一个整数,表示有多少 b∈[l,r]b\in[l,r]b∈[l,r] 可以使等式存在非负整数解。
输入输出样例 #1
输入 #1
2 5 10
3 5
输出 #1
5
说明/提示
对于 20%20\%20% 的数据,n≤5n \le 5n≤5,r≤10r \le 10r≤10。
对于 40%40\%40% 的数据,n≤10n \le 10n≤10,r≤106r \le 10^6r≤106。
对于 100%100\%100% 的数据,n≤12n \le 12n≤12,0≤ai≤5×1050 \le a_i \le 5\times 10^50≤ai≤5×105,1≤l≤r≤10121 \le l \le r \le 10^{12}1≤l≤r≤1012。
同余最短路
简化版
令n =3,l = 0,r = 101210^{12}1012
式子一:a0×x0+a1×x1+a1×x1+a2×x2a_0\times x_0 +a_1\times x_1 +a_1\times x_1 + a_2 \times x_2a0×x0+a1×x1+a1×x1+a2×x2。
式子二:a1×x1+a2×x2a_1\times x_1 + a_2 \times x_2a1×x1+a2×x2
式子一二中,aia_iai是已知值,xix_ixi是未知值。
性质一:如果式子二==b1式子二==b1式子二==b1存在自然数解,则式子一==b1+z×a0,z∈N式子一==b1+ z \times a0,z \in \N式子一==b1+z×a0,z∈N一定存在自然数解。
性质二:如果式子二==b1式子二==b1式子二==b1存在自然数解,且 b1<a0b1 < a0b1<a0,或式子二==b1−a0式子二==b1-a0式子二==b1−a0无自然数解。则任何y≡b1moda0y \equiv b1 \mod a_0y≡b1moda0
{式子一==y无自然数解y<b1式子一==y有自然数解其它\begin{cases} 式子一== y && 无自然数解 && y < b1 \\ 式子一==y && 有自然数解 && 其它 \\ \end{cases}{式子一==y式子一==y无自然数解有自然数解y<b1其它
利用图论求b1
有向图G,∀i∈[0,a0−1]\forall i \in [0,a_0-1]∀i∈[0,a0−1],都有两条出边:i→(i+a1)moda0,i→(i+a2)moda0i \rightarrow (i+a_1)\mod a_0,i \rightarrow (i+a_2)\mod a_0i→(i+a1)moda0,i→(i+a2)moda0。前者边权
a1a_1a1,后者边权a2a_2a2。
性质三: 式子二≡bmoda0式子二 \equiv b \mod a_0式子二≡bmoda0 有正整数解 ⟺\iff⟺ 图G存在0→(bmoda0)0 \rightarrow (b \mod a0)0→(bmoda0)的路径。
充分性证明:令u是第i条边的u起点,前x1x_1x1条边的终点 v=(u+a1)moda0v=(u+a_1)\mod a_0v=(u+a1)moda0,接着的x2x_2x2条边v=(u+a2)modx0v=(u+a_2) \mod x0v=(u+a2)modx0。
必要性证明:如果存在合法路径则:x1=边权a1的边的数量,x2=边权a2的边的数量x1=边权a_1的边的数量,x2=边权a_2的边的数量x1=边权a1的边的数量,x2=边权a2的边的数量。
结论:式子二≡imoda0式子二 \equiv i \mod a_0式子二≡imoda0的最小式子二为0→i0 \rightarrow i0→i的最短路。
实现
aia_iai升序排序,如果ai==0a_i==0ai==0删除。如果全部为0,则b只能是0。
按上述原理建边,求最短路。
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T1, class T2, class T3, class T4,class T5 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4,T5>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t);return in;
}template<class T1, class T2, class T3, class T4, class T5,class T6 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4, T5,T6>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >>get<5>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};//堆(优先队列)优化迪杰斯特拉算法 狄克斯特拉(Dijkstra)算法详解
typedef pair<long long, int> PAIRLLI;
class CHeapDis
{
public:CHeapDis(int n, long long llEmpty = LLONG_MAX / 10) :m_llEmpty(llEmpty){m_vDis.assign(n, m_llEmpty);}void Cal(int start, const vector<vector<pair<int, int>>>& vNeiB){std::priority_queue<PAIRLLI, vector<PAIRLLI>, greater<PAIRLLI>> minHeap;minHeap.emplace(0, start);while (minHeap.size()){const long long llDist = minHeap.top().first;const int iCur = minHeap.top().second;minHeap.pop();if (m_llEmpty != m_vDis[iCur]){continue;}m_vDis[iCur] = llDist;for (const auto& it : vNeiB[iCur]){minHeap.emplace(llDist + it.second, it.first);}}}vector<long long> m_vDis;const long long m_llEmpty;
};class Solution {
public:long long Ans(vector<int>& as, long long left, long long r) {sort(as.begin(), as.end(), greater<>());as.erase(unique(as.begin(), as.end()), as.end());while (as.size() && (0 == as.back())) {as.pop_back();}if (as.empty()) { return 0; }const int M = as.back();vector < vector<pair<int, int>>> neiBo(M);for (int node = 0; node < M; node++) {for (const auto& w : as) {neiBo[node].emplace_back((node + w) % M, w);}}CHeapDis dis(M);dis.Cal(0, neiBo);long long ans = 0;auto f = [&](long long lr, long long bMin) {if (lr < bMin) { return 0LL; }return 1 + (lr - bMin) / M;};for (int i = 0; i < M; i++) {if (-1 == dis.m_vDis[i]) { continue; }ans += f(r, dis.m_vDis[i]);ans -= f(left - 1, dis.m_vDis[i]);}return ans;}
};
int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUGios::sync_with_stdio(0); cin.tie(nullptr); cout.tie(nullptr); long long N,left,r;cin >> N >> left >> r ; auto as = Read<int>(N);
#ifdef _DEBUGprintf("left=%I64d,r=%I64d", left,r);Out(as, "as=");//Out(fun, ",fun=");//Out(que, ",que=");
#endif // DEBUGauto res = Solution().Ans(as,left,r);cout << res;return 0;
}
单元测试
long long left, r;vector<int> as;vector<tuple<int, int, int>> edge, vCloseInfo;TEST_METHOD(TestMethod11){left = 5, r = 10,as = { 3,5 };auto res = Solution().Ans(as, left, r);AssertEx(5LL, res);}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。