题目:
题解:
从简单开始吧。
首先我们假设题目让求的是“数列中所有数的【和】%mod=x的不同方案数”
那么设F[i][j]表示选择i个数%mod=j的方案数
转移方程
F[i][j]=∑|S|k=1F[i−1][j−Sk]
F
[
i
]
[
j
]
=
∑
k
=
1
|
S
|
F
[
i
−
1
]
[
j
−
S
k
]
这个形式我们可以转化为卷积的形式!
设b[i]表示第i个数字选了没0/1,令
a=F[i−1]
a
=
F
[
i
−
1
]
,
F′[i]=F[i]
F
′
[
i
]
=
F
[
i
]
那么柿子可以转化成
F′[i]=∑m−1j=0a[i−j]∗b[j]
F
′
[
i
]
=
∑
j
=
0
m
−
1
a
[
i
−
j
]
∗
b
[
j
]
这个玩意可以卷,可以发现卷一次是 F[i−1]−>F[i] F [ i − 1 ] − > F [ i ] ,那么我们卷n次就可以得到答案,可以用快速幂实现
那么我们转入正经的题目了,F的意义依然不变
转移方程
F[i][j]=∑|S|k=1F[i−1][jSk]
F
[
i
]
[
j
]
=
∑
k
=
1
|
S
|
F
[
i
−
1
]
[
j
S
k
]
我们这里引入【原根】
假设一个数g是P的原根,那么
gi%P
g
i
%
P
的结果两两不同,且有
1<g<P,0<i<P
1
<
g
<
P
,
0
<
i
<
P
,归根到底就是
g(P−1)=1(%P)
g
(
P
−
1
)
=
1
(
%
P
)
当且仅当指数为P-1的时候成立,P是素数。
我们可以发现一个素数只能有一个原根,并且这个原根从1~P-1次方可以取遍1~P-1所有的数字,这很显然,因为原根的次方项%P两两不同,只有P-1个,那肯定要取满P-1个数字咯
原根怎么求呢?从2开始枚举,然后暴力判断g^(P-1) = 1 (mod P)是否当且仅当指数为P-1的时候成立。
那么我们只要对于P找个原根g,这个乘除形式又可以变成加减了,比如说设
j=gx,Sk=gy
j
=
g
x
,
S
k
=
g
y
F[i][gx]=∑|S|k=1F[i−1][gx−y]
F
[
i
]
[
g
x
]
=
∑
k
=
1
|
S
|
F
[
i
−
1
]
[
g
x
−
y
]
同时我们的DP目标就变成了
F[n][gz]
F
[
n
]
[
g
z
]
,
gz=X
g
z
=
X
整理一下柿子:
F[i][x]=∑m−1y=0[gy∈S]F[i−1][x−y]
F
[
i
]
[
x
]
=
∑
y
=
0
m
−
1
[
g
y
∈
S
]
F
[
i
−
1
]
[
x
−
y
]
至此我们又画出来了加减的形式,可以运用刚才说的快速幂+NTT解决了。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const int mod=1004535809;
const int N=300005;
int n,r[N],m;LL a[N],b[N],f[N],ans[N],inv;bool vis[N];
LL ksm(LL a,LL k,int mod)
{
LL ans=1;
for (;k;k>>=1,a=a*a%mod)
if (k&1) ans=ans*a%mod;
return ans;
}
int yg(int m)
{
for (int i=2;i<=m;i++)
{
bool fff=0;
for (int j=1;j<m-1;j++)
if (ksm(i,j,m)==1) {fff=1;break;}
if (!fff) return i;
}
}
void NTT(LL *a,int id)
{
for (int i=0;i<n;i++)
if (i<r[i]) swap(a[i],a[r[i]]);
for (int k=1;k<n;k<<=1)
{
LL wn=ksm(3,(mod-1)/(k<<1),mod);
for (int i=0;i<n;i+=(k<<1))
{
LL w=1;
for (int j=0;j<k;j++,w=w*wn%mod)
{
LL x=a[i+j],y=w*a[i+j+k]%mod;
a[i+j]=(x+y)%mod; a[i+j+k]=(x-y+mod)%mod;
}
}
}
if (id==-1) reverse(a+1,a+n);
}
void cf(LL *f,LL *g)
{
for (int i=0;i<n;i++) a[i]=f[i];
for (int i=0;i<n;i++) b[i]=g[i];
NTT(a,1); NTT(b,1);
for (int i=0;i<=n;i++) a[i]=a[i]*b[i]%mod;
NTT(a,-1);
for (int i=0;i<n;i++) a[i]=a[i]*inv%mod;
for (int i=0;i<m-1;i++) f[i]=(a[i]+a[i+m-1])%mod;
}
void ksmNTT(int k)
{
ans[0]=1;
for (;k;k>>=1,cf(f,f))
if (k&1) cf(ans,f);
}
int main()
{
int c,x,S,s;scanf("%d%d%d%d",&c,&m,&x,&S);
int g=yg(m);int pos;
for (int i=1;i<=S;i++) scanf("%d",&s),vis[s]=1;
int now=1;
for (int i=0;i<m-1;i++,now=now*g%m)
{
if (vis[now]) f[i]=1;
if (now==x) pos=i;
}
int L=0;
for (n=1;n<=(m-1)*2;n<<=1) L++;
for (int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|((i&1)<<L-1);
inv=ksm(n,mod-2,mod);ksmNTT(c);
printf("%lld",ans[pos]);
}