zoukankan      html  css  js  c++  java
  • 2021牛客暑期多校 1H

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

    题意

    给定(n)个互不相同的范围在([0,500000])内的数

    要求求出最小的模数(seed),使得所有数与(seed)取模后仍是互不相同的

    思路(快速傅里叶变换)

    大部分队伍都是直接当想法题过掉的,本篇给出使用多项式乘法的解法

    首先,答案的最小值应是数字的数量(n),最大值应是数字的最大值(+1)

    所以得出(seedin[n,500001])(根据输入可以再缩小,但没必要)

    然后考虑本题要求

    假如我们当前选择了一个(seed),使得某两个数(a,b)对其取模后相同,即

    [a\%seed = b\%seed ]

    换言之,实际上(a)(b)的差值也就是(seed)的倍数,即

    [|a-b|\%seed=0 ]

    所以对于(seed)的选取,一定不能是任意两个数的差值(或者这个差值的因子)


    所以需要处理出这些数字两两之间的差值,这里可以借助多项式乘法将(O(n^2))的枚举优化成(O(nlogn))

    由于计算的是(a-b),并且(根据(FFT)板子易知)在多项式乘法中不允许出现负数下标(因为多项式乘法原本计算的是两两之和的种类数(a+b),而不是本题中两两之差)

    为了能让(a)(-b)能够分别存储,所以我们需要为每个数加上一个基础值(avg),使得(a+avg)(-b+avg)都在(0)以上

    显然,(avgge 500000)

    然后跑一遍(FFT),求出所有((a+avg)+(-b+avg))的种类数

    将得到的多项式提取出来,((a+avg)+(-b+avg)-avg imes 2)也就是(a-b)的值,将每种差值是否出现记录在(vis)数组中


    接下来就是最后一步,将所有出现的差值及其因子直接排除,选出最小的答案

    枚举因子可能需要(O(nsqrt{n}))可能会炸

    所以我们直接枚举(seedin[n,500001]),对于某个可能是答案的数(不是差值),找一下是否存在差值是其倍数(直接枚举倍数即可),如果没有差值是其倍数,则找到了答案



    #include<bits/stdc++.h>
    using namespace std;
    const int N=3000050;
    const double PI=acos(-1.0);
    const int avg=500000;
    int vis[500050];
    
    int lim=1,rev[N];
    struct cp
    {
        double x,y;
        cp(double u=0,double v=0){x=u,y=v;}
        friend cp operator +(const cp &u,const cp &v){return cp(u.x+v.x,u.y+v.y);}
        friend cp operator -(const cp &u,const cp &v){return cp(u.x-v.x,u.y-v.y);}
        friend cp operator *(const cp &u,const cp &v){return cp(u.x*v.x-u.y*v.y,u.x*v.y+u.y*v.x);}
    }f[N],g[N];
    
    void FFT(cp *a,int tp)
    {
        for(int i=0;i<lim;i++)
            if(i<rev[i])
                swap(a[i],a[rev[i]]);
        for(int md=1;md<lim;md<<=1)
        {
            cp rt=cp(cos(PI/md),tp*sin(PI/md));
            for(int stp=md<<1,pos=0;pos<lim;pos+=stp)
            {
                cp w=cp(1,0);
                for(int i=0;i<md;i++,w=w*rt)
                {
                    cp x=a[pos+i],y=w*a[pos+md+i];
                    a[pos+i]=x+y;
                    a[pos+md+i]=x-y;
                }
            }
        }
    }
    
    void initFFT(int n)
    {
        int lg=0;
        while(lim<=n)
            lg++,lim<<=1;
        for(int i=0;i<lim;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
    }
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int d;
            scanf("%d",&d);
            //记录d和-d
            f[avg+d].x+=1;
            g[avg-d].x+=1;
        }
        
        //根据乘法的值域预处理FFT蝴蝶变换,加快FFT速度
        initFFT(avg*4);
        
        //正常FFT流程
        FFT(f,1),FFT(g,1);
        for(int i=0;i<lim;i++)
            f[i]=f[i]*g[i];
        FFT(f,-1);
        
        //记录是否存在差值为i的情况(i+avg*2即多项式乘法内的结果下标)
        for(int i=1;i<=500001;i++)
            vis[i]=(int)round(f[i+avg*2].x/lim);
        
        //枚举seed,判断可行性
        for(int i=n;i<=500001;i++)
        {
            if(vis[i]>0)
                continue;
            for(int j=i+i;j<=500000;j+=i)
            {
                //如果存在差值j(i的倍数),则i不可行
                if(vis[j])
                {
                    vis[i]=1;
                    break;
                }
            }
            //i可行,作为模数输出即可
            if(vis[i]==0)
            {
                cout<<i<<'
    ';
                return 0;
            }
        }
        
        return 0;
    }
    

  • 相关阅读:
    API接口:分页
    PHP中判断变量为空的几种方法
    获取APP最新版本的接口案例
    浏览器兼容性
    APP的消息推送(极光推送)
    Json
    PHP 图片上传 (AIP图片上传接口)
    日历时间插件
    PHP读写文件
    ThinkPHP 事务处理 (事务回滚) 、异常处理
  • 原文地址:https://www.cnblogs.com/stelayuri/p/15024453.html
Copyright © 2011-2022 走看看