CDOJ 1314 Hash Perfectly

8 篇文章 0 订阅
题意:

给定 n 个数字,和一个hash表的最大长度,求一个最佳的hash表的长度,使得平均查找长度(ASL)最小,ASL=1nni=1ci

思路:

FFT + 思维题,很好很好的题,可惜我完全不会做!
这道题需要转几个弯!
1.平均查找长度的转换?
我们直接去求那个式子,是不太好搞的,所以转化一下为:

ASL=(k+k(k1)/2)

k 为表中某个单元冲突的数的个数,上式再转化一下就是 :
ASL=1+()/n

所以我们只需要关注每个hash表长度下,有冲突的总的数对是多少?

2.冲突的条件?
(ab) mod size=0 时,就会有冲突

3.计算所有可能的 a-b ?
也就是求 n 个数中,(a-b) 的可能值出现的次数,因为这样我们才能求冲突的对数!怎么优化呢?
FFT 开始正式展现它的作用了!

4.枚举hash表长度,计算对应的总的数对
有了前面的可能的 ab 出现的次数 cnt ,所以对于每个枚举的 长度 L ,统计满足 (a-b) % L == 0 的总的对数就OK了!
最后,记录下最小值及对应的hash表长度!

总的复杂度就是 O(nlogn)
柱爷爷 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值