Yet Another mod M
看到 \(N\) 这么小,容易想到枚举下标 \(i\) ,设 \(C[j]=|A[i]-A[j]|\),只要 \(M|C[j]\),那么 \(A[i]=A[j]\mod M\)。
同时因为若 \(p|M\),那么可以以 \(p\) 为答案。
所以可以对每个 \(C[j]\) 分解质因数,用 \(map\) 记录有多少个 \(C[j]\) 有某个质因子。
时间复杂度 \(O(N^2\sqrt{N}\log N)\) 显然会 T。
于是注意到我们只需要找到一种方案,而且从方案中任意一个数开始都可以求出具体方案,而且方案中元素个数很多(\(\frac{N}{2}\))那么就采用随机化算法,每次有 \(\frac{1}{2}\) 的概率成功,跑个 5、6 次绰绰有余。
(但说实话,若是 OI 中遇到这种题,没人敢写随机吧。。。
Notice:可以让随机次数随着 \(N\) 动态改变 ,比如设 \(T=\frac{100000}{N}\),这样充分利用时限
(注意!!! \(M!=2\),所以得让 \(4\) 充当质数,细节还是挺多的。。。)
#include<bits/stdc++.h>
using namespace std;
int a[5005],c[5005];
map<int, int> h;
int n;
inline int work(){
for(int i=2;i<=n;++i)c[i]=abs(a[i]-a[1]);
int maxx=0,id=-1;
h.clear();
for(int i=2;i<=n;++i){
int x=c[i];
for(int j=3;j*j<=c[i];++j){
if(x%j==0){
if(j>2){
h[j]++;
if(h[j]>maxx)maxx=h[j],id=j;
}
while(x%j==0)x/=j;
if(x==1)break;
}
}
if(x>2){
if(x%4!=0&&x%2==0)x/=2;
h[x]++;
if(h[x]>maxx)maxx=h[x],id=x;
}
}
if(maxx+1>n-maxx-1)return id;
else return -1;
}
int main(){
srand((unsigned)time(0));
cin>>n;
for(int i=1;i<=n;++i)cin>>a[i];
int ans=-1;
int T=100000/3/n;
while(T--){
int x=1ll*rand()*rand()%n+1;
swap(a[x],a[1]);
ans=max(ans,work());
if(ans>0)break;
}
cout<<ans<<endl;
return 0;
}
实际上,求质因子(遍历 \(\sqrt N\)) 和求所有约数(也是 \(\sqrt N\) 级别的)时间上差别不是很大,但求约数好写很多,所以若是无法线性筛预处理,直接求约数更方便。
#include<bits/stdc++.h>
using namespace std;
int a[5005],c[5005];
map<int, int> h;
int n;
inline int work(){
for(int i=2;i<=n;++i)c[i]=abs(a[i]-a[1]);
int maxx=0,id=-1;
h.clear();
for(int i=2;i<=n;++i){
int x=c[i];
for(int j=1;j*j<=c[i];++j){
if(x%j==0){
if(j>2){
h[j]++;
if(h[j]>maxx)maxx=h[j],id=j;
}
if(c[i]/j>2&&j*j!=c[i]){
h[c[i]/j]++;
if(h[c[i]/j]>maxx)maxx=h[c[i]/j],id=c[i]/j;
}
}
}
}
if(maxx+1>n-maxx-1)return id;
else return -1;
}
int main(){
srand((unsigned)time(0));
cin>>n;
for(int i=1;i<=n;++i)cin>>a[i];
int ans=-1;
int T=50000/3/n;
while(T--){
int x=1ll*rand()*rand()%n+1;
swap(a[x],a[1]);
ans=max(ans,work());
if(ans>0)break;
}
cout<<ans<<endl;
return 0;
}
好吧,看到一篇题解,才发现我是 **。。。
首先,我们的思路是找到 \(M\) 的可能答案。即只需要找到方案中的两个数,遍历 \(A[i]-A[j]\) 的质因子即可。仔细思考后,发现之前的枚举方法有问题。
若是当前枚举到了,那么我们得对 \(n-1\) 个数分解。若是没有枚举到,那么得一直枚举才能确保正确性。但明明有 \(\frac{n}{2}\) 个数,必有两个数相邻,只需要把所有的 \(A[i]-A[i+1]\) 分解即可。
更妙的是分解后的质因数个数最多是 \(10N\) 个,而且大多重复。
Notice:当出现数量的比例限制,如 \(\frac{n}{2},\frac{n}{3}\) 之类的,可以考虑鸽笼定理。(鸽笼定理也可以用随机化来解)。
浙公网安备 33010602011771号