[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;
}
posted @ 2022-09-12 18:52  P32sx  阅读(30)  评论(0)    收藏  举报