【做题笔记】ABC272G Yet Another mod M
Problem
题目大意:
给出一个长度 \(N\) 的序列 \(A\),其中 \(A_i\) 均为正整数且互不相同。
你需要选择一个范围在 \([3,10^9]\) 的正整数 \(M\),并执行一次下列操作:
- 将序列上的所有数替换为 \(A_i \bmod M\)。
若能找到一个数 \(M\) 使得序列中存在一个数 \(x\),且满足 \(A_i=x\) 的数量大于 \(A_i \not= x\) 的数量,输出这个 \(M\)。
Solution
人类智慧题。
随机选择两个数,钦定他们 \(\bmod M\) 后相等,则 \(M\) 为这两个数的差的因数。枚举 \(M\) 然后判断是否合法。
看着很不对劲?尝试求每次随机得到符合要求的 \(M\) 的概率。
因为 \(A_i=x\) 的数的数量严格大于二分之一,所以第一次随到 \(A_i=x\) 的 \(i\) 的概率是 \(\dfrac{1}{2}\),第二次也是 \(\dfrac{1}{2}\),所以每次随机能随到正确答案的概率为 \(\dfrac{1}{4}\)。
所以只需要随机很少次就可以得到正确答案。保险起见可以随机 \(200\) 次,复杂度为 \(O(\sqrt{A}\cdot n)\)。
Code
//Think twice,code once.
#include<map>
#include<ctime>
#include<cmath>
#include<random>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,a[5005],b[5005];
mt19937 gen(time(0));
int check(int x)
{
int num=0;
for(int i=1;i<=n;i++) num+=(b[i]%x==0);
return num>n/2;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int Case=1;Case<=200;Case++)
{
int p1=gen()%n+1,p2;
for(int i=1;i<=n;i++) b[i]=abs(a[p1]-a[i]);
//随机第一个数并求他和所有数的差,求差之后判断是否符合要求只需要看差 mod M 是否等于 0 即可。
do p2=gen()%n+1;
while(p1==p2);//随机第二个数
for(int j=1;j*j<=b[p2];j++)
if(b[p2]%j==0)//枚举差的因数
{
if(j>2&&check(j)){printf("%d\n",j);return 0;}
if(b[p2]/j>2&&check(b[p2]/j)){printf("%d\n",b[p2]/j);return 0;}
}
}
puts("-1");
return 0;
}