题目链接:https://www.luogu.com.cn/problem/P3321
题目大意:给定一个序列
S
S
S,从中选出任意
n
n
n个数,可重复、顺序相关,求乘积模
m
m
m下同余于
x
x
x的个数,保证
m
m
m为质数
初看题目,很难看出乘积如何处理。
但它保证了
m
m
m为质数,就可以联想到离散对数
将序列
S
S
S中每个数以及
x
x
x分别取离散对数,原问题等价成了选出
n
n
n个数相加,设个数的生成函数为
G
(
x
)
G(x)
G(x)
则:
G
(
x
)
=
(
∑
i
=
0
∞
x
i
)
n
(
i
∈
S
)
G(x)=(\sum_{i=0}^{\infty}x^i)^n\ \ (i\in S)
G(x)=(i=0∑∞xi)n (i∈S)
再用
N
T
T
NTT
NTT加速卷积即可
(该题多项式可以直接快速幂,不需要用牛顿迭代)
注意特判
a
[
i
]
=
0
a[i]=0
a[i]=0!!!
(
(
(代码第36行
)
)
)
注意特判
a
[
i
]
=
0
a[i]=0
a[i]=0!!!
(
(
(代码第36行
)
)
)
注意特判
a
[
i
]
=
0
a[i]=0
a[i]=0!!!
(
(
(代码第36行
)
)
)
一个特判一百分。。。
C o d e Code Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXM=8000,Mod=1004535809,G=3;
int a[MAXM+10],ind[MAXM+10],tot,f[MAXM<<2|10],invG;
inline int read();
namespace math{
int PFD(int);
int find_g(int);
int fastpow(int,int,int);
}
namespace ntt{
void get_mod(int*,int);
void fastpow(int*,int,int);
void MUL(int*,int*,int,int);
void NTT(int*,int*,int,int);
}
signed main(){
//freopen ("std.in","r",stdin);
//freopen ("std.out","w",stdout);
int n,m,x,s,g,l;
n=read(),m=read(),x=read(),s=read();
g=math::find_g(m);
l=g;
for (register int i=1;i<m;++i){
ind[l]=i%(m-1);
l=l*g%m;
}
x=ind[x];
for (register int i=1;i<=s;++i){
a[i]=read();
if (!a[i]) continue;
a[i]=ind[a[i]];
++f[a[i]];
}
ntt::fastpow(f,m-2,n);
printf("%lld\n",f[x]);
return 0;
}
inline int read(){
int x=0;
char c=getchar();
while (!isdigit(c))c=getchar();
while (isdigit(c))x=(x<<1)+(x<<3)+(c&15),c=getchar();
return x;
}
namespace math{
int PFD(int p,int *prime){
int tot=0;
for (register int i=2;p!=1;++i){
if (p%i==0) prime[++tot]=i;
while (p%i==0) p/=i;
}
return tot;
}
int find_g(int p){
static int prime[MAXM+10];
int tot=PFD(p-1,prime),tmp;
for (register int g=1;g<p;++g){
tmp=1;
for (register int i=1;i<=tot;++i){
if (fastpow(g,(p-1)/prime[i],p)==1){
tmp=0;
break;
}
}
if (tmp) return g;
}
return -1;
}
int fastpow(int x,int p,int mod=Mod){
int ans=1;
while (p){
if (p&1) ans=ans*x%mod;
x=x*x%mod;
p>>=1;
}
return ans;
}
}
namespace ntt{
void MUL(int *a,int *b,int n,int m){
static int f[MAXM<<2|10],g[MAXM<<2|10],tree[MAXM<<2|10];
for (register int i=0;i<=n;++i) f[i]=a[i]%Mod;
for (register int i=0;i<=m;++i) g[i]=b[i]%Mod;
m+=n; n=1;
while (n<=m) n<<=1;
int invn=math::fastpow(n,Mod-2); invG=math::fastpow(G,Mod-2);
for (register int i=1;i<n;++i) tree[i]= (tree[i>>1]>>1) | ((i&1)? n>>1 : 0);
NTT(f,tree,n,0),NTT(g,tree,n,0);
for (register int i=0;i<=n;++i) f[i]=f[i]*g[i]%Mod;
NTT(f,tree,n,invn);
for (register int i=0;i<=m;++i) a[i]=f[i];
for (register int i=0;i<=n;++i) f[i]=g[i]=0;
}
void NTT(int *f,int *tree,int n,int op){
for (register int i=0;i<n;++i)
if (i>tree[i]) swap(f[i],f[tree[i]]);
for (register int p=2;p<=n;p<<=1){
int len=p>>1;
int rg=math::fastpow(op? invG : G, (Mod-1)/p);
for (register int k=0;k<n;k+=p){
int buf=1;
for (register int l=k;l<k+len;++l){
int tmp=buf*f[l+len]%Mod;
f[l+len]=f[l]-tmp;
if (f[l+len]<0) f[l+len]+=Mod;
f[l]=(f[l]+tmp)%Mod;
buf=buf*rg%Mod;
}
}
}
if (op){
for (register int i=0;i<=n;++i)
f[i]=f[i]*op%Mod;
}
}
void fastpow(int *f,int n,int p){
static int ans[MAXM<<2|10];
for (register int i=0;i<=n;++i) ans[i]=0;
for (register int i=0;i<=n;++i) ans[i]=f[i];
--p;
while (p){
if (p&1) MUL(ans,f,n,n),get_mod(ans,n);
MUL(f,f,n,n),get_mod(f,n);
p>>=1;
}
for (register int i=0;i<=n;++i) f[i]=ans[i];
}
void get_mod(int *f,int n){
++n;
for (register int i=n;i<n+n;++i){
f[i%n]=(f[i%n]+f[i]) %Mod;
f[i]=0;
}
}
}