题意
从一个数列中找出一个子序列,使得子序列每个数模一个数同余,求最长长度。
首先我们容易发现,对于模数2,一个数要么余1,要么余0,那么一个序列至少有n/2个同余的。然后我们写一个式子
a m o d x = b m o d x a\mod x = b \mod x amodx=bmodx
a − b m o d x = 0 a-b \mod x = 0 a−bmodx=0
即a与b之差即同余的模数,这个余数可以被拆成很多质因数,也是可以使a,b同余的模数。
一个小与4e12的数最多能由11个不同的质因数相乘得(这步可以打表证明)。那么我们就能得到有可能是答案的模数。
再接着思考。我们每次取一个数,他们在答案序列中的几率p >= 1/2,那么取两个数,他们在答案中的概率为p2>=3/4,根据上面的推断,这两个数之差的每一个质因数是构成答案的模数的概率均为3/4,所以我们直接暴力枚举所有质因数x,计算x为模数时的最大答案。这个过程我们计算n次,每次随机两个数,则答案的准确度即为 ( 3 4 ) n (\frac{3}{4})^n (43)n。
#include "bits/stdc++.h"
using namespace std;
const int N = 4e5;
const int maxn = 3000100;
long long a[N];
long long prime[maxn];
bool sf[maxn];
void sushu() {
int num = 0;
memset(sf, true, sizeof(sf));
for (int i = 2; i < maxn; i++) {
if (sf[i]) prime[++num] = i;
for (int j = 1; j <= num; j++) {
if (1ll * i * prime[j] >= maxn) break;
sf[i * prime[j]] = false;
if (i % prime[j] == 0) break;
}
}
sf[1] = false;
sf[0] = false; //1 0 特判
}
int n;
int cal(long long mod,long long d)
{
int cnt = 0;
for (int i = 1; i <= n; ++i) {
cnt += ((a[i] % mod) == d);
}
return cnt;
}
void solve() {
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
int ti = 40;
int ans = 1;
while (ti--) {
int l,r;
while (1) {
l = rand() % n + 1;
r = rand() % n + 1;
if (l != r) break;
}
long long d = abs(a[r] - a[l]);
for (int i = 1;1ll * prime[i] * prime[i] <= d; ++i) {
if (d % prime[i] == 0)
{
ans = max(ans, cal(prime[i],a[l] % prime[i]));
while (d % prime[i] == 0) d /= prime[i];
}
}
if (d > 1){
ans = max(ans, cal(d,a[l] % d));
}
}
printf("%d\n",ans);
}
}
signed main() {
ios::sync_with_stdio(0);
srand((unsigned) time(NULL));
sushu();
solve();
}