题目:BZOJ3992.
题目大意:给定集合 S S S,求数列 a i a_i ai的数量,其中 ∏ i = 1 n a i ≡ x    ( m o d    m ) \prod_{i=1}^{n}a_i\equiv x\,\,(mod\,\,m) ∏i=1nai≡x(modm).
1 ≤ m ≤ 8000 , 1 ≤ n ≤ 1 0 9 , 1 ≤ x ≤ m 1\leq m\leq 8000,1\leq n\leq 10^9,1\leq x\leq m 1≤m≤8000,1≤n≤109,1≤x≤m,其中 m m m是个质数.
很熟练地设 f [ i ] [ j ] f[i][j] f[i][j]为前 i i i个数 ∏ k = 1 i a k ≡ j    ( m o d    m ) \prod_{k=1}^{i}a_k\equiv j\,\,(mod\,\,m) ∏k=1iak≡j(modm)的方案数,容易推出方程:
f [ i ] [ j ] = ∑ a b = j    ( m o d    m ) f [ i − 1 ] [ a ] ∗ f [ i − 1 ] [ b ] f[i][j]=\sum_{ab=j\,\,(mod\,\,m)}f[i-1][a]*f[i-1][b] f[i][j]=ab=j(modm)∑f[i−1][a]∗f[i−1][b]
发现这个东西跟卷积挺像,只是中间是乘法,事实上取对数就是加法了…
有了取对数这个想法,我们看看具体该如何操作.
首先在模意义下取对数的底数要取什么,想到这个底数 g g g应该要满足 g [ 0 , m − 2 ] g^{[0,m-2]} g[0,m−2]与 [ 1 , m − 1 ] [1,m-1] [1,m−1]一一对应,其中 m m m是个质数,突然发现原根具有这个性质.
那么我们用 g 0 , g 1 , g 2 , . . . , g m − 2 g^0,g^1,g^2,...,g^{m-2} g0,g1,g2,...,gm−2来代替 a a a,设 g A = a , g B = b , g J = j g^A=a,g^B=b,g^J=j gA=a,gB=b,gJ=j,并且重新定义 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个数 ∏ k = 1 i a k ≡ g j    ( m o d    m ) \prod_{k=1}^{i}a_k\equiv g^j\,\,(mod\,\,m) ∏k=1iak≡gj(modm).那么:
f [ i ] [ j ] = ∑ g A g B = g J    ( m o d    m ) f [ i − 1 ] [ A ] ∗ f [ i − 1 ] [ B ] = ∑ g A + B = g J    ( m o d    m ) f [ i − 1 ] [ A ] ∗ f [ i − 1 ] [ B ] f[i][j]=\sum_{g^Ag^B=g^J\,\,(mod\,\,m)}f[i-1][A]*f[i-1][B]\\ =\sum_{g^{A+B}=g^J\,\,(mod\,\,m)}f[i-1][A]*f[i-1][B] f[i][j]=gAgB=gJ(modm)∑f[i−1][A]∗f[i−1][B]=gA+B=gJ(modm)∑f[i−1][A]∗f[i−1][B]
根据欧拉定理的推论, a b ≡ a b    m o d    ϕ ( m ) ≡ a b    m o d    ( m − 1 )    ( m o d    m ) a^{b}\equiv a^{b\,\,mod\,\,\phi(m)}\equiv a^{b\,\,mod\,\,(m-1)}\,\,(mod\,\,m) ab≡abmodϕ(m)≡abmod(m−1)(modm).
那么可以得到:
f [ i ] [ j ] = ∑ A + B ≡ J    ( m o d    ( m − 1 ) ) f [ i − 1 ] [ A ] ∗ f [ i − 1 ] [ B ] f[i][j]=\sum_{A+B\equiv J\,\,(mod\,\,(m-1))}f[i-1][A]*f[i-1][B] f[i][j]=A+B≡J(mod(m−1))∑f[i−1][A]∗f[i−1][B]
这样我们就把乘法转化成了加法,得到了一个卷积形式,其中模 m − 1 m-1 m−1可以通过卷积后得到的多项式中第 i + m − 1 i+m-1 i+m−1项加到第 i i i项实现.
然后我们发现这个式子每一次转移都是恒定的向量,所以可以快速幂计算.
多项式快速幂时间复杂度 O ( m log m log n ) O(m\log m\log n) O(mlogmlogn),原根时间复杂度 O ( m + g log 2 m ) = O ( m log 2 m ) O(\sqrt{m}+g\log^2 m)=O(m\log^2m) O(m+glog2m)=O(mlog2m),总复杂度 O ( m log m ( log m + log n ) ) O(m\log m(\log m+\log n)) O(mlogm(logm+logn)).
代码如下:
#include
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=16384;
const LL mod=1004535809;
int n,m,s,x;
int pr[N+9],tp;
int power(int a,int k,int p){
int s=1;
for (;k;k>>=1,a=a*a%p)
if (k&1) s=s*a%p;
return s;
}
int get_proot(int n){
if (n==2) return 1;
int phi=n-1,v=phi;
for (int i=2;i*i<=phi;++i)
if (v%i==0){
pr[++tp]=i;
while (v%i==0) v/=i;
}
if (v>1) pr[++tp]=v;
bool flag;
for (int g=2;g<n;++g){
flag=0;
for (int i=1;i<=tp;++i)
if (power(g,phi/pr[i],n)==1) {flag=1;break;}
if (flag) continue;
return g;
}
}
LL power(LL a,LL k){
LL s=1;
for (;k;k>>=1,a=a*a%mod)
if (k&1) s=s*a%mod;
return s;
}
int rev[N+9],len,l;
LL inv,w[N+9];
void pre_rev(){
len=1;l=0;
while (len<=m<<1) len<<=1,++l;
for (int i=0;i<len;++i)
rev[i]=rev[i>>1]>>1|(i&1)<<l-1;
inv=power((LL)len,mod-2);
}
void ntt(LL *a,int len,int t){
for (int i=0;i<len;++i)
if (i<rev[i]) swap(a[i],a[rev[i]]);
LL g=3,x,y,wn;
if (t==-1) g=power(g,mod-2);
for (int i=1;i<len;i<<=1){
wn=power(g,(mod-1)/(i<<1));w[0]=1;
for (int j=1;j<i;++j)
w[j]=w[j-1]*wn%mod;
for (int j=0;j<len;j+=i<<1)
for (int k=0;k<i;++k){
x=a[j+k];y=w[k]*a[j+k+i]%mod;
a[j+k]=x+y;a[j+k+i]=x-y;
if (a[j+k]>=mod) a[j+k]-=mod;
if (a[j+k+i]<0) a[j+k+i]+=mod;
}
}
}
void suf_a(LL *a){
for (int i=0;i<=len;++i)
a[i]=a[i]*inv%mod;
int tmp;
for (int i=m-1;i<=len;++i)
tmp=i%(m-1),a[tmp]=(a[tmp]+a[i])%mod,a[i]=0;
}
void sqr(LL *a){
ntt(a,len,1);
for (int i=0;i<=len;++i)
a[i]=a[i]*a[i]%mod;
ntt(a,len,-1);
suf_a(a);
}
void mul(LL *a,LL *b){
ntt(a,len,1);
ntt(b,len,1);
for (int i=0;i<=len;++i)
a[i]=a[i]*b[i]%mod;
ntt(a,len,-1);
ntt(b,len,-1);
for (int i=0;i<=len;++i)
b[i]=b[i]*inv%mod;
suf_a(a);
}
LL t[N+9],f[N+9];
int mp[N+9];
Abigail into(){
scanf("%d%d%d%d",&n,&m,&x,&s);
int v,g=get_proot(m),p=1;
for (int i=0;i<m-1;++i)
mp[p]=i,p=p*g%m;
for (int i=1;i<=s;++i){
scanf("%d",&v);
if (v) ++f[mp[v]];
}
}
Abigail work(){
pre_rev();
t[mp[1]]=1;
for (;n;n>>=1,sqr(f))
if (n&1) mul(t,f);
}
Abigail outo(){
printf("%lld\n",t[mp[x]]);
}
int main(){
into();
work();
outo();
return 0;
}