[ARC-148] A - mod M
原题链接
题意
给你一个序列 \(A = (A_1, A_2, ... ,A_n)\) ,你需要对它们进行一次操作
你可以选择一个大于等于 \(2\) 的模数 \(m\) , 然后将 \(A\) 序列的所有元素更新为 \(A_i\) \(mod\) \(m\)
举个例子, 你可以选择 \(m = 4\) 并将 \(( 2, 7, 4)\) 更新为 \((2, 3, 0)\)
现在,你的任务是寻找一个合适的 \(m\) 并输出序列 \(A\) 被 \(m\) 更新后的不同元素的个数
思路
首先,我们考虑 \(m = 2\) 的特殊情况
由于取模运算的封闭性,当 \(m = 2\) 时, 显然答案最大为 \(2\)
因此我们证明出答案的上界为 \(2\),而由于更新后 \(A\) 数组至少有一个元素,即答案的下界为 \(1\)
故我们可以知道答案只会在 \(1,2\) 两者之中
我们考虑什么时候答案可以为 \(1\)
我们来寻找一个 \(A\) 数组和 \(m\),并让答案为 \(1\) :
当 \(A_i\) \(mod\) \(m\) 均等于 \(x\) 时,答案为 \(1\)
那么我们可以将 \(A_i\) 写作 \(A_i = k*m + x\)
于是有 \(A_i-A_{i-1} = (k_i-k_{i-1})*m\),避免负数,我们把 \(|A_i-A_{i-1}|\) 写作 \(a_i\)
可以知道当 \(i\) 从 \(2\) 取到 \(n\) 时,得到的 \(a\) 序列,他们的最大公倍数 \(gcd\) 的下界为 \(m\)
至此,接下来我们来寻找 \(a\) 序列的最小公倍数作为模数 \(m\)
- 当 \(gcd = 0\) 时
那么根据辗转相除法,此时所有的 \(A_i\) 都是相等的,显然答案为 \(1\)
- 当 \(gcd = 1\) 时
那么此时他们就没有除了 \(1\) 公约数,也就没有一个合适的 \(m\) ,于是令 \(m = 2\) ,所以答案为 \(2\)
- 对于其他情况
则是此时存在一个 \(m\), 使得更新后的 \(A_i\) 均为 \(x\) ,所以答案为 \(1\)
至此,我们可以在 \(O(Nlog_{max(A[i])})\) 的时间复杂度内,解决该问题
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, a[N], b[N];
int main(){
cin >> n;
for(int i = 1 ; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 2; i <= n; i++)
b[i] = abs(a[i] - a[i - 1]);
int gcd = b[2];
for(int i = 3; i <= n; i++)
gcd = __gcd(b[i], gcd);
if(gcd == 1)
puts("2");
else puts("1");
return 0;
}