20200412 T2 带mod的FFT
程序员文章站
2022-05-22 19:23:30
...
题目描述:
给定数组和,定义:
求第大的。
保证为质数。(原题目并没有强调这一点,而是给出了N的表格,坑)
题目分析:
首先特判的情况,然后考虑的贡献。
注意到 都是质数,使用原根把 转化为
设(其中是除去时的贡献)
那么有
枚举的取值,转换成艾弗森表达式:
将翻转后变成卷积形式:
,这里的虽然带mod,仍然可以FFT,对于一个固定的 ,产生贡献的 可以这么看(下面的B表示如果那一位等于k则为1否则为0,A省去了第二维):
前两个多项式相乘时,前者会加到第项,后者会加到项,并且可以发现这样不会漏算重算。所以。(中括号表示第几项的系数)
Code:
#include<bits/stdc++.h>
#define maxn 524305
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const double Pi = acos(-1);
struct cp{
double r,i; cp(){}
cp(double r,double i):r(r),i(i){}
cp operator + (const cp &t)const{return cp(r+t.r,i+t.i);}
cp operator - (const cp &t)const{return cp(r-t.r,i-t.i);}
cp operator * (const cp &t)const{return cp(r*t.r-i*t.i,r*t.i+i*t.r);}
}A[maxn],B[maxn],C[maxn],w[maxn];
int r[maxn];
void FFT(cp *a,int len,int flg){
for(int i=0;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int i=2,l=1;i<=len;l=i,i<<=1){
cp wn = cp(cos(2*Pi/i),sin(2*Pi*flg/i));
for(int k=l-2;k>=0;k-=2) w[k+1]=(w[k]=w[k>>1])*wn;
for(int j=0;j<len;j+=i)
for(int k=j;k<j+l;k++){
cp u=a[k],v=w[k-j]*a[k+l];
a[k]=u+v,a[k+l]=u-v;
}
}
if(flg==-1) for(int i=0;i<len;i++) a[i].r/=len;
}
int n,m,K,a[maxn][4],b[maxn],c[maxn],pw[maxn],G;
int p[maxn],cnt;
inline int Pow(int a,int b,const int mod){
int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
return s;
}
int prime_root(const int N){
int x=N-1;
for(int i=2;i*i<=x;i++) if(x%i==0) {p[++cnt]=i;while(x%i==0) x/=i;}
if(x>1) p[++cnt]=x;
for(int g=2;;g++){
bool flg=1;
for(int i=1;i<=cnt;i++) if(Pow(g,(N-1)/p[i],N)==1) {flg=0;break;}
if(flg) return g;
}
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
read(n),read(m),read(K),G=prime_root(n);
for(int i=(pw[0]=1);i<n;i++) pw[i]=1ll*pw[i-1]*G%n;
for(int i=0;i<m;i++) for(int j=0;j<n;j++) read(a[j][i]);
for(int i=0;i<n;i++) read(b[i]);
for(int i=0;i<n;i++) c[0]+=a[i][b[0]];
for(int i=1;i<n;i++) c[i]+=a[0][b[0]];
int len=1; while(len<=(n-2)<<1) len<<=1; w[0]=cp(1,0);
for(int i=0;i<len;i++) r[i]=r[i>>1]>>1|(i&1?len>>1:0);
for(int k=0;k<m;k++){
for(int i=0;i<len;i++) A[i]=B[i]=cp(0,0);
for(int i=0;i<n-1;i++) A[i].r=a[pw[i]][k],B[i].r=(b[pw[i]]==k);
reverse(B,B+n-1);
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=0;i<n-1;i++) c[pw[i]]+=round(C[n-2-i].r)+round(C[n-2-i+n-1].r);
}
sort(c,c+n);
printf("%d\n",c[n-K]);
}
拓展:
求
同样,翻转 ,然后分开来看,大致对应下图:
即