【做题笔记】ABC272G Yet Another mod M

Problem

ABC272G Yet Another mod M

题目大意:

给出一个长度 \(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;
}
posted @ 2022-10-21 13:32  Mine_King  阅读(42)  评论(0编辑  收藏  举报