牛客多校① H-Hash Function【NTT】

题意:
给出一个包含 N N N个正整数的集合 S S S,求一个最小的 P P P 使 S i   % P S_i\ \% P Si %P得到 N N N个不同的结果(即不产生哈希冲突)
题解:
  ∵ a % P ! = b % P \ \because a\%P!=b\%P  a%P!=b%P
∴ a b s ( a − b ) % P ! = 0 \therefore abs(a-b)\%P!=0 abs(ab)%P!=0
∴ ∀ a , b ∈ S , a b s ( a − b ) % P ! = 0 \therefore \forall a,b\in S,abs(a-b)\%P!=0 a,bS,abs(ab)%P!=0
所以只要枚举 P P P ,判断 k ∗ P ( k ∈ Z + ) k*P (k\in Z^+) kP(kZ+)是否是 S i − S j S_i-S_j SiSj的差值,但是如果枚举 i , j i,j i,j求差值时间是 O ( n 2 ) O(n^2) O(n2)。不难推出 a − b = ( 500000 − b ) + a − 500000 a-b=(500000-b)+a-500000 ab=(500000b)+a500000,令 A i = [ i ∈ S ] , B i = [ 500000 − i ∈ S ] A_i=[i\in S],B_{i}=[500000-i\in S] Ai=[iS]Bi=[500000iS],用 NTT求一下 A A A B B B的卷积就好了(具体为什么可以自己推一下),时间是 O ( n l o g n ) O(nlogn) O(nlogn)

#include<iostream>
#include<sstream>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#include<set>
#include<vector>
#include<stack>
#include <utility>
#include<algorithm>
#include<cstdio>
#include<list>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<time.h>
#define int long long
#define PI acos(-1.0)
#define eps 1e-9
#define lowbit(a) ((a)&-(a))
const int mod = 998244353;
using namespace std;
inline int read(){
   char c=getchar();int x=0,s=1;
   while(c<'0'||c>'9'){if(c=='-')s=-1;c=getchar();}
   while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
   return x*s;
}
int qpow(int a,int b){
   int ans=1;
   while(b){
   	if(b&1)ans=(ans*a)%mod;
   	a=(a*a)%mod;
   	b>>=1;
   }
   return ans;
}
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int N = 5e6+10;
const int maxn = 1e3+10;


int c[N],a[N],b[N];
int limit=1,L,rev[N],G=3,Gi=332748118;

void NTT(int *A,int type){//NTT加速

   for(int i=0;i<limit;i++) if(i<rev[i])swap(A[i],A[rev[i]]);
   
   for(int mid=1;mid<limit;mid<<=1){
   	int Wn=qpow(type==1?G:Gi , (mod-1)/(mid<<1));
   	for(int j=0;j<limit;j+=(mid<<1)){
   		int w=1;
   		for(int k=0;k<mid;k++,w=(w*Wn)%mod){
   			int x=A[j+k],y=(w*A[j+mid+k])%mod;
   			A[j+k]=(x+y)%mod;
   			A[j+mid+k]=(x-y+mod)%mod;
   		}
   	}
   }
}

signed main(){
   std::ios::sync_with_stdio(false);
   cin.tie(0),cout.tie(0);
   int n; cin>>n;
   for(int i=1;i<=n;i++){
   	cin>>c[i]; 
   	a[c[i]]++;
   	b[500000-c[i]]++;
   }
   while(limit<=1000000)limit<<=1,L++;
   for(int i=0;i<limit;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1)) ;
   
   NTT(a,1); NTT(b,1);
   for(int i=0;i<limit;i++)a[i]=(a[i]*b[i])%mod;
   NTT(a,-1);
   
   int inv=qpow(limit,mod-2);
   for(int i=0;i<=1000000;i++)a[i]=(a[i]*inv)%mod;
   for(int i=1;i<=500005;i++){
   	int flag=0;
   	for(int j=i+500000;j<=1000000;j+=i)flag|=a[j];
   	/*
   	因为A[i][j]B卷积是i+j=i+(500000-x ),有一半<=500000,一半>=500000,
   	但是<=500000的部分是反着的,所以取>=500000的一半就好了
   	*/
   	if(!flag){
   		cout<<i<<endl; break;
   	}
   }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值