2021牛客暑期多校训练营1 H.Hash Function

https://ac.nowcoder.com/acm/contest/11166/H

题意:

给n个数,让你找出一个最小的正整数m满足:这n个数%m得到的值各不相同。0<=a[i]<=500000。1<=n<=500000。

官方题解:

 

 

 两个数a,b。若他们%m后余数相同,则a%m=b%m,可得(a-b)%m==0。题目要求各个数余数不同,那么就要任意两个数的差d,d%m!=0。也就是上述的找到最小的m,使其不是任意两个数的差的约数。

如果能知道任意两个数的差的集合,那么就可以直接枚举答案来判断,m是不是某个差的约数,也就是用d%m!=0来判断m是不是符合要求。

要求任意两个数的差的集合,直接暴力枚举的话,复杂度是O(n2)。考虑把他转换成多项式乘法的形式。

若存在的数集合为s,那么把他取反后得到的集合称为-s。

系数为1表示存在,指数为i表示当前的数为i(相当于把题目的数放在多项式的指数里面)。

把集合s用多项式表示有A(x)={p0+p1X+p2X2....},pi=1表示存在数i。同理,

取反后的集合-s用多项式表示有B(x)={p500000-0X500000-0+p500000-1X500000-1+p500000-2X500000-2....}。因为数组不能存负数,所以加上偏移量500000。

之后考虑A(x)*B(x),A(x)的每一项都与B(x)的每一项系数相乘,指数相加。系数相乘为1表示两个数都存在,指数相加得到的和就是两个数的差+偏移量。乘法就是任意两项都相乘,所以一次乘法就能得到任意两数的差组成的多项式。

多项式乘法用FFT加速,之后枚举答案。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include<bits/stdc++.h>
using namespace std;
const int N = 1500700;
const int MX = 500003;//偏移量要略大于5e5
const double PI = acos(-1);
struct Complex//复数类,自己定义常数小。
{
    double x, y;
    Complex operator+ (const Complex& t) const
    {
        return {x + t.x, y + t.y};
    }
    Complex operator- (const Complex& t) const
    {
        return {x - t.x, y - t.y};
    }
    Complex operator* (const Complex& t) const
    {
        return {x * t.x - y * t.y, x * t.y + y * t.x};
    }
}a[N], b[N];
struct FFT
{
    int rev[N], bit=0, tot=0;
    void fft(Complex a[], int inv)
    {
        for (int i = 0; i < tot; i ++ )
            if (i < rev[i])//按分治后的结果排序
                swap(a[i], a[rev[i]]);
        for (int mid = 1; mid < tot; mid <<= 1)
        {
            auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)});
            for (int i = 0; i < tot; i += mid * 2)
            {
                auto wk = Complex({1, 0});
                for (int j = 0; j < mid; j ++, wk = wk * w1)
                {
                    auto x = a[i + j], y = wk * a[i + j + mid];
                    a[i + j] = x + y, a[i + j + mid] = x - y;
                }
            }
        }
    }
    void work(){
        int n=MX,m=MX;
        while ((1 << bit) < n + m + 1) bit ++;
        tot = 1 << bit;
        for (int i = 0; i < tot; i ++ )//预处理奇偶分组分治后的结果
            rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
        fft(a, 1), fft(b, 1);//正向变换,系数表示变为点表示。
        for (int i = 0; i < tot; i ++ ) a[i] = a[i] * b[i];//点表示法直接相乘
        fft(a, -1);//逆向变换,点表示变成系数表示。
        for (int i = 0; i <= n + m; i ++ )
            a[i].x = (int)(a[i].x / tot + 0.5);
    }
};
int p[N],vis[N];
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++) cin>>p[i];
    for(int i=1;i<=n;i++){
        a[p[i]].x=1;//+p[i]
        b[MX-p[i]].x=1;//-p[i]
    }
    struct FFT solve;solve.work();//p[i]和-p[i]乘积
    for(int i=0;i<=MX;i++){
        vis[i]=a[i+MX].x;//得到任意出现的差值
    } 
    int ans;
    for(int i=1;i<=MX;i++){//枚举答案i
        bool ok=1;
        for(int j=i;j<=MX;j+=i){
            //枚举i倍数,只要i不是任意差值的约数,i就是答案
            if(vis[j]){
                ok=0;break;
            }
        }
        if(ok){
            printf("%d\n",i);
            break;
        }
    }
    return 0;
}

 

posted on 2021-07-18 16:45  Aminers  阅读(122)  评论(0编辑  收藏  举报

导航