乘法并不容易用 FFT 或 NTT 维护,考虑在模意义下化乘为加。
化乘为加主要有两种方法:\(\log\) 和 \(\gamma\)(指标),由于在取模意义下,所以使用后者。
那剩下的部分就是快速幂,用 NTT 加速即可。时间复杂度 \(O(m\log m\log n)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=50005,p=1004535809;
namespace NTT{int rev[N],mx,k,m,qp,h;struct dft{int fg[N];};int qpow(int x,int y,int p){int re=1;while(y){if(y&1) re=re*x%p;x=x*x%p,y>>=1;}return re;}void init(int n){mx=1,k=0,rev[0]=0,h=m-1;while(mx<=n) mx*=2,k++;for(int i=0;i<mx;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));qp=qpow(mx,p-2,p);}void fmd(dft &a){for(int i=m-1;i<mx;i++) if(a.fg[i])a.fg[i%h]=(a.fg[i%h]+a.fg[i])%p,a.fg[i]=0;}void ntt(dft &a,int fl){for(int i=0;i<mx;i++)if(i<rev[i]) swap(a.fg[i],a.fg[rev[i]]);for(int i=1;i<mx;i*=2){int om=qpow(fl?3:(p+1)/3,(p-1)/(i<<1),p);for(int j=0,w=1;j<mx;j+=i*2,w=1)for(int k=j;k<j+i;k++,w=w*om%p){int x=a.fg[k],y=w*a.fg[k+i]%p;a.fg[k]=(x+y)%p,a.fg[k+i]=(x-y+p)%p;}}if(fl) return;for(int i=0;i<mx;i++)a.fg[i]=a.fg[i]*qp%p;}
}using namespace NTT;
int n,ed,ln,g,ru[N];
dft re,now;vector<int>pr;
int prirt(int x){x--;int cc=x;for(int i=2;i*i<=x;i++){if(cc%i==0) pr.push_back(i);while(cc%i==0) cc/=i;if(cc==1) continue;}if(cc>1) pr.push_back(cc);for(int i=1,fl=1;i<=x;i++,fl=1){for(auto j:pr)if(qpow(i,x/j,m)==1){fl=0;break;}if(fl) return i;}return 0;
}signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n>>m>>ed>>ln,init(m*2);re.fg[0]=1,g=prirt(m);for(int i=1,mi=1;i<m;i++)ru[mi]=i-1,mi=mi*g%m;for(int i=1,x;i<=ln;i++){cin>>x;if(!x) continue;now.fg[ru[x]]=1;}while(n){ntt(now,1);if(n&1){ntt(re,1);for(int i=0;i<mx;i++)re.fg[i]=re.fg[i]*now.fg[i]%p;ntt(re,0),fmd(re);}for(int i=0;i<mx;i++)now.fg[i]=now.fg[i]*now.fg[i]%p;ntt(now,0),fmd(now),n>>=1;}cout<<re.fg[ru[ed]];return 0;
}