2021“MINIEYE杯”中国大学生算法设计超级联赛(9)

题目链接


8 Integers Have Friends 2.0(数论)

描述
给出 n ( 2 ≤ n ≤ 2 ∗ 1 0 5 ) n (2\leq n\leq2*10^5) n(2n2105) 个互不相同的数 a i ( 1 ≤ a i ≤ 4 ∗ 1 0 12 ) a_i(1\leq a_i\leq 4*10^{12}) ai(1ai41012)。对于一个整数 m ≥ 2 m\geq2 m2,如果 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 aiaj 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=aiaj,模数 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} 41012 内的数最多只有 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} 105,已经非常小了。

代码

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值