8 Integers Have Friends 2.0(数论)
描述:
给出
n
(
2
≤
n
≤
2
∗
1
0
5
)
n (2\leq n\leq2*10^5)
n(2≤n≤2∗105) 个互不相同的数
a
i
(
1
≤
a
i
≤
4
∗
1
0
12
)
a_i(1\leq a_i\leq 4*10^{12})
ai(1≤ai≤4∗1012)。对于一个整数
m
≥
2
m\geq2
m≥2,如果
a
c
1
=
a
c
2
=
.
.
.
=
a
c
k
(
m
o
d
m
)
a_{c_1}=a_{c_2}=...=a_{c_k}\ \ (mod\ m)
ac1=ac2=...=ack (mod m),那么这些元素是在一个集合的。问可能产生的集合中,元素最多的集合元素个数是多少?
思路:
①考虑
m
m
m 取
2
2
2 时,
n
n
n 个元素被划分成了两类,所以最后答案集合的大小至少是
⌈
n
2
⌉
⌈\frac{n}{2}⌉
⌈2n⌉ 。
②若
a
i
=
a
j
(
m
o
d
m
)
a_i=a_j \ (mod \ m)
ai=aj (mod m),则
a
i
−
a
j
a_i-a_j
ai−aj 是
m
m
m 的倍数。
③最优解的
m
m
m 一定包含质数。
对于③粗略证明:
假设
a
i
=
a
j
(
m
o
d
p
q
)
a_i=a_j\ (mod\ pq)
ai=aj (mod pq),即
a
i
m
o
d
p
q
=
a
j
m
o
d
p
q
=
r
a_i\ mod\ pq=a_j\ mod\ pq = r
ai mod pq=aj mod pq=r,
那么有
a
i
=
p
q
k
i
+
r
,
a
j
=
p
q
k
j
+
r
a_i=pqk_i+r,\ a_j=pqk_j+r
ai=pqki+r, aj=pqkj+r
即
a
i
=
p
(
q
k
i
+
⌊
r
p
⌋
)
+
<
r
>
p
,
a
j
=
p
(
q
k
j
+
⌊
r
p
⌋
)
+
<
r
>
p
a_i=p(qk_i+⌊\frac{r}{p}⌋)+<r>_p,\ \ a_j=p(qk_j+⌊\frac{r}{p}⌋)+<r>_p
ai=p(qki+⌊pr⌋)+<r>p, aj=p(qkj+⌊pr⌋)+<r>p
所以
a
i
=
a
j
(
m
o
d
p
)
a_i=a_j\ (mod\ p)
ai=aj (mod p)。
也就是说模上一个合数在一个集合中的元素,模上这个数的最小质因子也是在一个集合中的,集合的大小只增不减。
证毕!
考虑随机取两个不同的下标索引
i
,
j
i,\ j
i, j,
a
i
a_i
ai与
a
j
a_j
aj都处在最优解的集合中的概率是不低于
1
4
\frac{1}{4}
41 的。如果
a
i
a_i
ai 与
a
j
a_j
aj 都在最优解的集合中,令
v
a
l
=
∣
a
i
−
a
j
∣
val=|a_i-a_j|
val=∣ai−aj∣,模数
m
m
m 一定是
v
a
l
val
val 的因数,并且我们可以只考虑
v
a
l
val
val 的质因数。所以可以
O
(
m
a
x
a
i
)
O(\sqrt{max{a_i}})
O(maxai)枚举
v
a
l
val
val 的质因子
m
m
m,因为
4
∗
1
0
12
4*10^{12}
4∗1012 内的数最多只有
11
11
11 个不同的质因数,所以可以
O
(
11
n
)
O(11n)
O(11n) 来计算取每个质因数作为模数,并且包含
a
i
a_i
ai 与
a
j
a_j
aj 的集合大小,然后更新答案即可。
重复上述操作
K
K
K 次,时间复杂度是
O
(
K
m
a
x
a
i
+
11
K
n
)
O(K\sqrt{max{a_i}}+11Kn)
O(Kmaxai+11Kn),并且没有找到最优解的概率是
(
3
4
)
K
(\frac{3}{4})^K
(43)K,当
K
K
K 取
40
40
40 时,这个概率约等于
1
0
−
5
10^{-5}
10−5,已经非常小了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200010, M = 2000010, K = 40;
int n;
ll a[N];
int v[M], prime[M], m;
mt19937 gen(time(0));
void primes(int n)
{
for (int i = 2; i <= n; i++)
{
if (!v[i])
v[i] = i, prime[++m] = i;
for (int j = 1; j <= m; j++)
{
if (prime[j] > v[i] || prime[j] * i > n) break;
v[prime[j] * i] = prime[j];
}
}
}
int calc(ll m, ll r)
{
int ans = 0;
for (int i = 1; i <= n; i++)
ans += a[i] % m == r;
return ans;
}
int main()
{
primes(M - 5);
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
int ans = 1;
for (int i = 1; i <= K; i++)
{
int x, y;
while (1)
{
x = gen() % n + 1;
y = gen() % n + 1;
if (x != y) break;
}
ll val = abs(a[x] - a[y]);
for (int j = 1; (ll)prime[j] * prime[j] <= val; j++)
{
if (val % prime[j] == 0)
{
ans = max(ans, calc(prime[j], a[x] % prime[j]));
while (val % prime[j] == 0)
val /= prime[j];
}
}
if (val > 1)
ans = max(ans, calc(val, a[x] % val));
}
printf("%lld\n", ans);
}
return 0;
}