H Hash Function
题目描述
给定一个存在n个非负整数的数组S
,找出一个最小的模数mod
,使得S
中任意一个数字取模的结果都不相同。
意思等同于:找出一个最小的mod
,使得Hash不会产生冲突。
思路
暴力计算是复杂度是 n 2 n^2 n2显然不行。
对于任意的i, j: S i % m o d = a i ; S j % m o d = a j S_i\ \% \ mod = a_i; \ S_j\ \% \ mod = a_j Si % mod=ai; Sj % mod=aj
如果使得
a
i
≠
a
j
a_i \neq a_j
ai=aj:
∣
S
i
−
S
j
∣
%
m
o
d
≠
0
|S_i-S_j|\ \%\ mod \neq 0
∣Si−Sj∣ % mod=0,即mod
不能是 数组中任意两个数差的绝对值 的约数。
这里需要考虑如何用更低的复杂度求出 某个值是否为数组中任意两个数差的绝对值 。
-
先说一下如何快速求出 某个值是否为数组中任意两个数的和 。
假设 n = 3 n = 3 n=3, S = { 1 , 2 , 4 } S=\{1, 2, 4\} S={1,2,4}。
多项式
A
为: A ( x ) = x 1 + x 2 + x 4 A(x) = x^1+x^2+x^4 A(x)=x1+x2+x4;多项式B
为: B ( x ) = x 1 + x 2 + x 4 B(x) = x^1+x^2+x^4 B(x)=x1+x2+x4。多项式
A
和B
相乘(卷积):
C ( x ) = A ( x ) ∗ B ( x ) = x 1 ∗ ( x 1 + x 2 + x 4 ) + x 2 ∗ ( x 1 + x 2 + x 4 ) + x 4 ∗ ( x 1 + x 2 + x 4 ) = x 2 + 2 ∗ x 3 + x 4 + 2 ∗ 2 5 + 2 ∗ 2 6 + x 8 \begin{aligned} C(x) &= A(x)*B(x) \\ &= x^1*(x^1+x^2+x^4)+x^2*(x^1+x^2+x^4)+x^4*(x^1+x^2+x^4) \\ &= x^2+2*x^3+x^4+2*2^5+2*2^6+x^8 \end{aligned} C(x)=A(x)∗B(x)=x1∗(x1+x2+x4)+x2∗(x1+x2+x4)+x4∗(x1+x2+x4)=x2+2∗x3+x4+2∗25+2∗26+x8
可以发现,[2,3,4,5,6,8]为任意 S i + S j S_i+S_j Si+Sj的可能值(i可以等于j)。
但是暴力去相乘的话,复杂度还是
n
2
n^2
n2,所以需要通过FFT/NTT
来对卷积操作进行加速(复杂度是O(nlogn))。
-
求 某个值是否为数组中任意两个数差的绝对值 的操作和上述操作基本相同。
只需要让多项式
B
表为: B ( x ) = x − 1 + x − 2 + x − 4 B(x) = x^{-1}+x^{-2}+x^{-4} B(x)=x−1+x−2+x−4。但是因为存在负值不好处理,需要加上一个偏移量
P=6
(P自己定义就行了),将多项式转变为 B ( x ) = x 5 + x 4 + x 2 B(x) = x^{5}+x^{4}+x^{2} B(x)=x5+x4+x2。多项式
A
和B
相乘(卷积):
C ( x ) = A ( x ) ∗ B ( x ) = x 1 ∗ ( x 5 + x 4 + x 2 ) + x 2 ∗ ( x 5 + x 4 + x 2 ) + x 4 ∗ ( x 5 + x 4 + x 2 ) = x 3 + x 4 + x 5 + 3 ∗ 2 6 + 2 7 + x 8 + x 9 \begin{aligned} C(x) &= A(x)*B(x) \\ &= x^1*(x^5+x^4+x^2)+x^2*(x^5+x^4+x^2)+x^4*(x^5+x^4+x^2) \\ &= x^3+x^4+x^5+3*2^6+2^7+x^8+x^9 \end{aligned} C(x)=A(x)∗B(x)=x1∗(x5+x4+x2)+x2∗(x5+x4+x2)+x4∗(x5+x4+x2)=x3+x4+x5+3∗26+27+x8+x9
将得到的数组减去偏移量P
取绝对值,[0,1,2,3]为任意
S
i
+
S
j
S_i+S_j
Si+Sj的可能值(i可以等于j)
得到数组中任意两个数差的绝对值的所有值之后,只需要从小到大遍历mod
可能的值就行了。
AC代码
#include<bits/stdc++.h>
using namespace std;
const double PI = acos(-1);
const int P = 500001;// 偏移量
const int N = 1<<21;
int limit, bit;
bool vis[N];
int R[N];
// 复数的板子
struct Complex {
double x, y;
Complex (double x = 0, double y = 0) : x(x), y(y) { }
} a[N], b[N];
Complex operator * (Complex J, Complex Q) {
return Complex(J.x * Q.x - J.y * Q.y, J.x * Q.y + J.y * Q.x);
}
Complex operator - (Complex J, Complex Q) {
return Complex(J.x - Q.x, J.y - Q.y);
}
Complex operator + (Complex J, Complex Q) {
return Complex(J.x + Q.x, J.y + Q.y);
}
//FFT板子
void FFT(Complex * A, int type) {
for(int i = 0; i < limit; ++ i)
if(i < R[i])
swap(A[i], A[R[i]]);
for(int mid = 1; mid < limit; mid <<= 1) {
Complex wn(cos(PI / mid), type * sin(PI / mid));
for(int len = mid << 1, pos = 0; pos < limit; pos += len) {
Complex w(1, 0);
for(int k = 0; k < mid; ++ k, w = w * wn) {
Complex x = A[pos + k];
Complex y = w * A[pos + mid + k];
A[pos + k] = x + y;
A[pos + mid + k] = x - y;
}
}
}
if(type == 1) return ;
for(int i = 0; i <= limit; ++ i)
a[i].x /= limit, a[i].y /= limit;
}
bool check(int x) {
for (int i = x; i <= P; i += x) {
if (vis[i] == true) return false;
}
return true;
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin.exceptions(ios::badbit | ios::failbit);
int n, tmp;
cin>>n;
for(int i = 0; i < n; i++) {
cin>>tmp;
a[tmp].x = 1;
b[P - tmp].x = 1;
}
bit = 20;// 只要大于2*p+1就行
limit = 1 << bit;
// 预处理
for(int i = 0; i < limit; i++)
R[i] = (R[i >> 1] >> 1) | ((i & 1) << (bit - 1));
// ===================================
FFT(a, 1);
FFT(b, 1);
for(int i = 0; i < limit; i++) {
a[i] = a[i] * b[i];
}
FFT(a, -1);
// ===================================
for (int i = 0; i < limit; i++) {
int x = (int)(a[i].x + 0.5);
if(x > 0)
vis[abs(i - P)] = 1;
}
for(int i = n; i < P+1; i++) {
if(check(i)) {
cout<<i<<endl;
break;
}
}
return 0;
}
补充
FFT
通过将多项式的系数表示
转变成点值表示
,再用两个多项式的点值表示
相乘,将得到的点值表示
逆向转变成系数表式
,最后得到的表达式就是卷积之后的多项式。