题意:
给定 n 个数字,和一个hash表的最大长度,求一个最佳的hash表的长度,使得平均查找长度(ASL)最小,
ASL=1n∑ni=1ci
思路:
FFT + 思维题,很好很好的题,可惜我完全不会做!
这道题需要转几个弯!
1.平均查找长度的转换?
我们直接去求那个式子,是不太好搞的,所以转化一下为:
ASL=∑(k+k∗(k−1)/2)
k 为表中某个单元冲突的数的个数,上式再转化一下就是 :
ASL=1+∑(冲突对数)/n
所以我们只需要关注每个hash表长度下,有冲突的总的数对是多少?2.冲突的条件?
当 (a−b) mod size=0 时,就会有冲突3.计算所有可能的 a-b ?
也就是求 n 个数中,(a-b) 的可能值出现的次数,因为这样我们才能求冲突的对数!怎么优化呢?
FFT 开始正式展现它的作用了!4.枚举hash表长度,计算对应的总的数对
有了前面的可能的 a−b 出现的次数 cnt ,所以对于每个枚举的 长度 L ,统计满足 (a-b) % L == 0 的总的对数就OK了!
最后,记录下最小值及对应的hash表长度!总的复杂度就是
O(n∗logn)
柱爷爷 Orz
代码:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int N = 6e5 + 10;
const double PI = acos(-1.0);
struct Complex
{
double real, image;
Complex(double _real, double _image)
{
real = _real;
image = _image;
}
Complex(){real = 0, image = 0;}
};
Complex operator + (const Complex &c1, const Complex &c2)
{
return Complex(c1.real + c2.real, c1.image + c2.image);
}
Complex operator - (const Complex &c1, const Complex &c2)
{
return Complex(c1.real - c2.real, c1.image - c2.image);
}
Complex operator * (const Complex &c1, const Complex &c2)
{
return Complex(c1.real*c2.real - c1.image*c2.image, c1.real*c2.image + c1.image*c2.real);
}
int rev(int id, int len)
{
int ret = 0;
for(int i = 0; (1 << i) < len; i++)
{
ret <<= 1;
if(id & (1 << i)) ret |= 1;
}
return ret;
}
Complex ret[N];
void FFT(Complex* a, int n, int f)
{
for(int i = 0;i < n;++ i) ret[rev(i, n)] = a[i];
//从递归的最后一层向上计算
for(int s = 1; (1 << s) <= n;++ s)
{
int m = (1 << s);
Complex wm = Complex(cos(f*2*PI/m), sin(f*2*PI/m));
for(int k = 0;k < n;k += m)
{
Complex w = Complex(1, 0);
for(int j = 0;j < (m >> 1); ++j)
{
Complex t = w * ret[k + j + (m >> 1)];
Complex u = ret[k + j];
ret[k + j] = u + t;
ret[k + j + (m >> 1)] = u - t;
w = w * wm;
}
}
}
if(f == -1) for(int i = 0;i < n;++ i) ret[i].real /= n, ret[i].image /= n;
for(int i = 0;i < n; ++i) a[i] = ret[i];
}
const int M = 2e5 + 5;
int n, m, a[M];
Complex A[N], B[N], C[N];
LL ans[M];
int main()
{
scanf("%d%d", &n, &m);
int MX = 0;
for(int i = 1;i <= n;i ++) {
scanf("%d", &a[i]);
MX = max(MX, a[i]);
}
for(int i = 1;i <= n;i ++) {
A[a[i]].real += 1, B[MX - a[i]].real += 1;
}
int len = 0, res;
for(int i = 0;len < 2 * MX;i ++) len = (1<<i);
FFT(A, len, 1);
FFT(B, len, 1);
for(int i = 0;i < len;i ++) C[i] = A[i] * B[i];
FFT(C, len, -1);
for(int i = 1;i <= MX;i ++) ans[i] = LL(C[i + MX].real + 0.5);
LL val = 1e18;
for(int i = 1;i <= m;i ++) {
LL cnt = 0;
for(int j = i;j <= MX;j += i) cnt += ans[j];
if(cnt < val) {
val = cnt;
res = i;
}
}
printf("%d\n", res);
return 0;
}