[SDOI2015]序列统计
这题神仙的一批
看到乘法显然并不是很好做,尝试将其转化为加法
乘法变加法最常见的方式就是取\(log\)
\[\prod_{i=1}^na_i\equiv x(mod\ P)
\]
就变成了
\[\sum_{i=1}^n log(a_i)\equiv log(x)(mod\ P)
\]
但是在模意义下取\(log\)显然不是很可行啊
其实只需要找到一个合理的底数就好了
所以我们只需要找到原根就好了
如果\(g\)是\(P\)的原根,那么\(g^0,g^1,g^2....g^{\varphi(P)-1}\)这\(\varphi(P)\)个数肯定两两不同,又因为\(\varphi(P)=P-1\),所以\(g\)的幂次方恰好能表示\(mod\ P\)意义下的所有整数
于是我们就把相乘转化成了指数相加
我们设一个多项式\(f(x)\),\(f(x)\)表示\(x\)这个指数出现的次数
我们让\(f\)自己卷一下
\[f^2(x)=\sum_{i=0}^xf(i)f(x-i)
\]
发现如果我们暴力\(dp\)求一下两个指数相加形成的方案也是这个柿子
于是我们就可以用卷积来代替暴力的背包\(dp\)
答案就是\(f^n(x)\)
需要一个多项式快速幂
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 100005
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const LL Mod=1004535809;
const LL g[2]={3,334845270};
int N,mod,T,n,len=1;
int rev[maxn],tax[maxn];
LL a[maxn],b[maxn],S[maxn],C[maxn];
inline LL quick(LL a,LL b) {LL S=1;while(b) {if(b&1ll) S=S*a%Mod;b>>=1;a=a*a%Mod;}return S;}
inline void NTT(LL *f,int o) {
for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
for(re int i=2;i<=len;i<<=1) {
int ln=i>>1;LL og1=quick(g[o],(Mod-1)/i);
for(re int l=0;l<len;l+=i) {
LL og=1,t;
for(re int x=l;x<l+ln;x++) {
t=(og*f[x+ln])%Mod;
f[x+ln]=(f[x]-t+Mod)%Mod;
f[x]=(f[x]+t)%Mod;
og=(og*og1)%Mod;
}
}
}
if(!o) return;
LL inv=quick(len,Mod-2);
for(re int i=0;i<len;i++) f[i]=(f[i]*inv)%Mod;
}
inline void mul(LL *A,LL *B) {
for(re int i=0;i<len;i++) C[i]=B[i];
NTT(A,0),NTT(C,0);
for(re int i=0;i<len;i++) A[i]=(A[i]*C[i])%Mod;
NTT(A,1);
for(re int i=mod-1;i<len;i++) A[i%(mod-1)]=(A[i%(mod-1)]+A[i])%Mod,A[i]=0;
}
inline int check(int x) {
memset(tax,0,sizeof(tax));
int now=1;
for(re int i=0;i<mod-1;i++) {
if(tax[now]) return 0;tax[now]=i;
now=(now*x)%mod;
}
return 1;
}
inline void Quick(LL B) {
S[0]=1;
while(B) {
if(B&1ll) mul(S,b);
B>>=1ll;
mul(b,b);
}
}
int main()
{
N=read(),mod=read(),T=read(),n=read();
for(re int i=2;i<mod;i++)
if(check(i)) break;
for(re int i=1;i<=n;i++) a[i]=read(),a[i]%=mod;
for(re int i=1;i<=n;i++)
if(a[i]) b[tax[a[i]]]++;
while(len<=mod+mod) len<<=1;
for(re int i=0;i<len;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)?len>>1:0);
Quick(N);
printf("%lld\n",S[tax[T]]);
return 0;
}