【SDOI2015】BZOJ3992 序列统计题解(DP+原根+NTT)

题目: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=1naix(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 1m8000,1n109,1xm,其中 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=1iakj(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[i1][a]f[i1][b]

发现这个东西跟卷积挺像,只是中间是乘法,事实上取对数就是加法了…

有了取对数这个想法,我们看看具体该如何操作.

首先在模意义下取对数的底数要取什么,想到这个底数 g g g应该要满足 g [ 0 , m − 2 ] g^{[0,m-2]} g[0,m2] [ 1 , m − 1 ] [1,m-1] [1,m1]一一对应,其中 m m m是个质数,突然发现原根具有这个性质.

那么我们用 g 0 , g 1 , g 2 , . . . , g m − 2 g^0,g^1,g^2,...,g^{m-2} g0,g1,g2,...,gm2来代替 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=1iakgj(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[i1][A]f[i1][B]=gA+B=gJ(modm)f[i1][A]f[i1][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) ababmodϕ(m)abmod(m1)(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+BJ(mod(m1))f[i1][A]f[i1][B]

这样我们就把乘法转化成了加法,得到了一个卷积形式,其中模 m − 1 m-1 m1可以通过卷积后得到的多项式中第 i + m − 1 i+m-1 i+m1项加到第 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;
}

你可能感兴趣的:(【SDOI2015】BZOJ3992 序列统计题解(DP+原根+NTT))