2021 牛客暑期多校训练赛1 H - Hash Function

2021 牛客暑期多校训练赛1 H - Hash Function

题目链接:牛客多校2 H题 - Hash Function

题意

给定一个序列 a n a_n an要求寻找一个最小的模数使得对序列 a n a_n an的所有数对该数取模后没有冲突(即取模后不存在两个相同的数)

分析

设最小模数为 p p p,要使 a n a_n an p p p取模后不存在冲突,则不存在 a j m o d    p ≡ a j m o d    p a_j \mod p \equiv a_j \mod p ajmodpajmodp,即 ∣ a i − a j ∣ m o d    p ≡ 0 |a_i-a_j| \mod p \equiv 0 aiajmodp0

由此题意转化为求最小的 p p p,使得 p p p和它的倍数不为任意两个 a i a_i ai a j a_j aj差值

也就是说,我们要在题目规定的时间范围内求得所有 ∣ a i − a j ∣ |a_i-a_j| aiaj,由于 N = 5 e 5 N=5e5 N=5e5,所以 O ( N 2 ) O(N^2) O(N2)的做法会爆掉,(然而出题人在比赛时居然没把另一种暴力的做法卡掉,被很多人水了过去,结果赛后加强了数据,把我们这些拉题自训的给卡了orz)

所以应该用FFT/NTT来处理,把复杂度降到 O ( N log ⁡ N ) O(N\log N) O(NlogN)

我们可以用类似生成函数的思想来构造一个多项式,设 b i ∈ { 0 , 1 } b_i \in \{0,1\} bi{0,1},当且仅当 i i i a a a中出现过时, b i = 1 b_i=1 bi=1,令:

B ( x ) = ∑ i = 0 n b i x i B(x)=\sum_{i=0}^{n}b_ix^i B(x)=i=0nbixi

C ( x ) = ∑ i = 0 n c i x i = ∑ i = 0 n b n − i x i C(x)=\sum_{i=0}^nc_ix^i=\sum_{i=0}^nb_{n-i}x^i C(x)=i=0ncixi=i=0nbnixi

D ( x ) = B ( x ) C ( x ) = ∑ i = 0 2 n d i x i D(x)=B(x)C(x)=\sum_{i=0}^{2n}d_ix^i D(x)=B(x)C(x)=i=02ndixi

其中

d i = ∑ j = 0 i b i c i − j = ∑ j = 0 i b j b n − i + j d_i=\sum_{j=0}^ib_ic_{i-j}=\sum_{j=0}^ib_jb_{n-i+j} di=j=0ibicij=j=0ibjbni+j

d i > 0 d_i>0 di>0的含义为,至少存在一对 j j j n − i + j n-i+j ni+j同时在 a a a中出现过,即存在 u , v u,v u,v使得 ∣ a u − a v ∣ = i − n |a_u-a_v|=i-n auav=in,那么我们用FFT求出 D ( x ) = B ( x ) C ( x ) D(x)=B(x)C(x) D(x)=B(x)C(x)后,枚举 [ n , 2 n ] [n, 2n] [n,2n]之间的 d i d_i di的值,就可以求出 [ 0 , n ] [0,n] [0,n]内每个值是否为 a a a中某两个数的差值,这样就可以在 O ( N log ⁡ N ) O(N \log N) O(NlogN)的时间内求解

参考:【题解】2021牛客暑期多校第一场 H.Hack function

倾心讲解 FFT 多项式与快速傅里叶变换 && 迭代模板带注释

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>

const int N = 500005, FFTN = N << 2;
const double PI = acos(-1);

struct Complex {
    double x, y;
    Complex() {}
    Complex(double _x, double _y) : x(_x), y(_y) {}
    Complex operator + (const Complex &r) const {
        return Complex(x + r.x, y + r.y);
    }
    Complex operator - (const Complex &r) const {
        return Complex(x - r.x, y - r.y);
    }
    Complex operator * (const Complex &r) const {
        return Complex(x*r.x - y*r.y, x*r.y+y*r.x);
    }
} a[FFTN], b[FFTN];

int n;

int fft_n = 1, fft_m, R[FFTN];

void init() {
    int len = N << 1; //**//
    while(fft_n <= len) fft_n <<= 1, ++fft_m;
    for(int i = 0; i < fft_n; ++i)
        R[i] = (R[i>>1]>>1) | ((i&1) << (fft_m-1));
}

void FFT(Complex *A, int f) {
    for(int i = 0; i < fft_n; ++i) {
        if(i < R[i]) std::swap(A[i], A[R[i]]);
    }
    for(int i = 1; i < fft_n; i <<= 1) {
        Complex wn(cos(PI/i), f*sin(PI/i));
        for(int j = 0, r = (i << 1); j < fft_n; j += r) {
            Complex w(1, 0);
            for(int k = 0; k < i; ++k, w = w * wn) {
                Complex x = A[j+k], y = w * A[i+j+k];
                A[j+k] = x + y, A[i+j+k] = x - y;
            }
        }
    }
    if(f == -1) {
        for(int i = 0; i < fft_n; ++i)
            A[i].x /= fft_n;
    }
}

bool vis[N<<1];

int main() {
    scanf("%d", &n);
    for(int i = 0, val; i < n; ++i) {
        scanf("%d", &val);
        a[val].x = 1;
        b[N-val].x = 1;
    }
    init();
    FFT(a, 1); FFT(b, 1);
    for(int i = 0; i < fft_n; ++i)
        a[i] = a[i] * b[i];
    FFT(a, -1);
    for(int i = N; i <= (N << 1); ++i)
        vis[i-N] = int(a[i].x +  0.5);
    for(int i = n; i <= N; ++i) {
        bool flag = false;
        for(int j = i; j <= N; j += i)
            flag |= vis[j];
        if(!flag) {
            printf("%d", i);
            break;
        }
    }
    return 0;
}
一些没什么用的注意事项
  • 抄完 写完FFT板子后在本地运行发现居然跑出了2000ms+,慢的要死,放在队友电脑上跑出来也是一样的慢,我寻思题目要求2s这能过?于是把代码放牛客上交了一发300ms+过了,相距将近10倍,差点以为见鬼了。后来在各种试错+探索下发现本地加上O3优化(就是代码打头那句)后也能跑出300ms+,搞半天是牛客自动开启了O3优化,良心OJ了属于是
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值