同余方程有解的条件 && Cyclic Cipher题解
一.结论
对于 个同余方程组,如果两两有解,则一定有解满足 个方程组。
证明:(归纳法)
1.初始状态
attention:这里(初始状态部分)的 都表示 , 都表示 ,就不大写了
我们先只考虑三个同余方程组.
假设三个同余方程组:(条件)
由中国剩余定理,有以下基本结论:
引理1:
考虑质因子 , 它在 中幂次分别为 。
则
暴力分类讨论 的相对大小,容易得出 。
引理2:
整除的传递性
引理3:
考虑质因子 ,它在 中幂次分别为 。
则由条件得:
则:
则:
所以
对于所有的质因子,都有,那么 即
证明:
令
则
即证: (扩展中国剩余定理)
即证: (扩展中国剩余定理)
即证: (引理1)
即证: (引理3)
我们只考虑 , 同理
即证:
即证:
即证:
由引理2(整除的传递性):
即证:
①:已证,在基本条件处
②: 的性质
③:易证
得证。
2.归纳过程
attention: 这里 表示第 个到第 个方程合并后的方程。
,则表明合并后 个方程后,有解。
欲证:若 且方程两两有解, 。
考虑第 个方程
则:由初始状态的证明,有解同时满足第 , , 个方程。
由扩展剩余定理,则第 和 个方程合并后,还能与第 个方程合并。
令第 和 个方程合并后的方程为 ,则 和 有解。
所以任意方程都与 有解,所以 , 合并后,如果有解能满足这 个同余方程(),则有解能满足这 个方程()
二.实现
有了结论,这道题就非常好做了。
因为操作时间非常长,大于 倍 ,所以一个周期里的情况都会出现。
枚举最长串的数字是 ,如果在时间 , 这段子串同时出现 ,则 满足 ( 表示第 个序列的长度, 表示 在第 个序列出现的位置(从零开始编号))。
则我们跑所有 出现的行。
我们考虑用双指针 。
- 如果发现第 和第 不相邻,则 。
因为都不连续了,所以 可以直接赋为
- 长度(即模数)最大为 ,所以方程最多有 个 (如果模数一样,但是余数不一样,则无解)。
我们用 保存下来所有同余方程,每次 。
假设我们现在的 对应的长度为 , 出现在 号位置。
-
如果 没出现过,就暴力扫一遍所有的同余方程,看看都能不能和新方程()合并,不满足就 ,直到与之矛盾的方程消失为止。
-
如果 出现过,就判断新余数和之前出现的模数相同的方程的余数是否相同,不相同就 ,直到没有模数为 的方程(除了这个新的方程) 。
但是!!!由于 可以判断出余数不同的情况,所以我们就不需要写这个判断了(写了代码也不长)。
时间复杂度均摊下来是:
三.参考代码
#include <map>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) (make_pair (x, y))
template <typename T> void read (T &x) { x = 0; T f = 1;char tem = getchar ();while (tem < '0' || tem > '9') {if (tem == '-') f = -1;tem = getchar ();}while (tem >= '0' && tem <= '9') {x = (x << 1) + (x << 3) + tem - '0';tem = getchar ();}x *= f; return; }
template <typename T> void write (T x) { if (x < 0) {x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0'); }
template <typename T> void print (T x, char ch) { write (x); putchar (ch); }
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
const int Maxn = 1e5;
const int Maxlen = 40;
int n, m;
int len[Maxn + 5];
int a[Maxn + 5][Maxlen + 5];
vector <PII> v[Maxn + 5];
int bak[Maxn + 5], b[Maxn + 5];
LL gcd (LL x, LL y) {
if (y == 0) return x;
else return gcd (y, x % y);
}
bool check (int m1, int b1, int m2, int b2) {
return (b1 - b2) % gcd (m1, m2) == 0;
}
int main () {
read (n); read (m);
for (int i = 1; i <= n; i++) {
read (len[i]);
for (int j = 0; j < len[i]; j++)
{ read (a[i][j]); v[a[i][j]].push_back (MP (i, j)); }
}
for (int i = 1; i <= m; i++) {
memset (bak, 0, sizeof bak);
int l = 0, r = 0, ans = 0;
while (r < (int)v[i].size ()) {
int idx = v[i][r].first, Mod = len[idx], p = v[i][r].second;
if (r != 0 && idx != v[i][r - 1].first + 1)
while (l < r)
bak[len[v[i][l++].first]]--;
for (int j = 1; j <= 40; j++) {
if (bak[j] >= 1 && check (j, b[j], Mod, p) == 0) {
while (bak[j])
{ bak[len[v[i][l++].first]]--; }
}
}
bak[Mod]++;
b[Mod] = p;
ans = Max (ans, r - l + 1);
r++;
}
printf ("%d\n", ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现