Hash Function (https://ac.nowcoder.com/acm/contest/11166/H)
题意:
对于给定的集合\(S=\{a_0,a_1,...,a_{n-1}\}\),找到最小的正整数\(x\),使得所有数对\(x\)取模的结果互不相同。
其中 \(1 \le n \le 500000, 0 \le a_i \le 5000000, a_i \ne a_j\)
解法:
事后诸葛亮,这题做法是卷积。
求出\(a_i\)之间的差都有哪些,然后这些差的因数都不能是x。筛掉这些因数后最小数就是\(x\)。
如何筛掉差的因数呢?可以看出\(a_i\)的范围不大,先枚举因数\(i\),再枚举倍数\(k\)即可,时间复杂度\(O(n/1+n/2+...+n/n)=O(nlogn)\)
如何求出所有差值呢?卷积。\(\{a_0,a_1,...,a_{n-1}\}\)和\(\{a_{1-n},a_{2-n},...,a_0\}\)用01序列表示(比如说,vis[i]=0集合中没有i,vis[i]=1表示集合中有i),求fft加速的卷积即可求出\(b_k=\sum_{i=0}^{n-1}a_i\times a_{k-i}\),时间复杂度\(O(nlogn)\)
这是我第一次写fft和卷积,因此留下模板供以后使用:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=500000;
const double Pi=acos(-1);
//fft complex struct
struct Complex{
double x, y;
Complex(double x=0, double y=0):x(x),y(y){}
Complex operator+(Complex b){ return Complex(x+b.x,y+b.y); }
Complex operator-(Complex b){ return Complex(x-b.x,y-b.y); }
Complex operator*(Complex b){ return Complex(x*b.x-y*b.y,x*b.y+y*b.x); }
};
//fft varity define
int n, m;
Complex a[N+10<<2], b[N+10<<2], c[N+10<<2];
int r[N+10<<2];
//fft by butterfly
void fft(Complex *cm, LL cnum, LL tag){
for(int i=0; i<cnum; ++i) if(i<r[i]) swap(cm[i],cm[r[i]]);
for(int mid=1; mid<cnum; mid<<=1){
Complex wk=Complex(cos(2*Pi/(2*mid)),tag*sin(2*Pi/(2*mid)));
for(int j=0; j<cnum; j+=2*mid){
Complex w(1,0);
for(int k=0; k<mid; ++k){
Complex buf=w*cm[j+k+mid];
cm[j+k+mid]=cm[j+k]-buf;
cm[j+k]=cm[j+k]+buf;
w=w*wk;
}
}
}
}
//problem
int an;
bool vis[N+N+20];
int main(){
cin>>an;
for(int i=1, u; i<=an; ++i){
scanf("%d", &u);
a[u].x=1;
b[N-u].x=1;
}
//fft butterfly generation
int dgt=1, bits=0;
while(dgt<=N+N) { dgt<<=1; bits++; }
for(int i=0; i<dgt; ++i) r[i]=(r[i>>1]>>1)|((i&1)<<(bits-1));
//fft
fft(a,dgt,1);
fft(b,dgt,1);
for(int i=0; i<=dgt; ++i){
c[i]=a[i]*b[i];
}
fft(c,dgt,-1);
//problem
for(int i=0; i<=N+N; ++i){
vis[i]=((int)(c[i].x/dgt+0.5))!=0;
}
for(int i=N; i<=N+N; ++i){
vis[i]|=vis[N+N-i];
}
for(int i=0; i<=N; ++i){
vis[i]=vis[i+N];
}
vis[N+1]=false;
for(int i=1; i<=N+1; ++i){
bool ok=true;
for(int j=i; j<=N+1&&ok; j+=i){
if(vis[j]) ok=false;
}
if(ok){
printf("%d\n", i);
break;
}
}
return 0;
}