LOJ#3058. 「HNOI2019」白兔之舞 单位根反演+矩阵乘法+MTT
假如说每次只能跳一步,一共跳 $i$ 步到达 $(i,x)$ 的方案数是好求的:
$f[i][j]=\sum_{k=1}^{n} f[i-1][k] \times w(k,j)$.
时间复杂度为 $O(Ln^2)$.
$ans_{t}=\sum_{i=0}^{L} f_{L,y} \times \binom{L}{i} [i \mod k=t]$
考虑求解 $f_{L,y}$ 时运用矩阵乘法,然后对后面的特判进行单位根反演:
$ans_{t}=\sum_{i=0}^{L} F^i[x,y] \binom{L}{i}\frac{1}{k}\sum_{j=0}^{k-1}w_{k}^{j(i-t)}$
$ans_{t}=\frac{1}{k} \sum_{j=0}^{k-1} w_{k}^{-tj}\sum_{i=0}^{L}F^i[x,y]\binom{L}{i}w_{k}^{ij}$
后面部分可以写成二项式定理的形式,即 $(Fw_{k}^{j}+I)^L$
那么 $ans_{t}=\frac{1}{k} \sum_{j=0}^{k-1}w_{k}^{-tj}(Fw_{k}^{j}+I)^L$
令 $c_{j}=(Fw_{k}^{j}+I)^L[x,y]$,这个可以提前都预处理出来.
则 $ans_{t}=\frac{1}{k} \sum_{j=0}^{k-1} w_{k}^{-tj}c_{j}$
这里面 $-tj$ 是一个乘法的形式,没法再优化了.
有结论 $ij=\binom{i+j}{2}-\binom{i}{2}-\binom{j}{2}$
这样就没有相乘的形式了.
最后 $ans_{t}=\frac{w_{k}^{\binom{t}{2}}}{k}\sum_{j=0}^{k-1}w_{k}^{\binom{j}{2}}w_{k}^{-\binom{t+j}{2}}c_{j}$
这个可以直接用 MTT 来算.
但是这里面 $k$ 并不是 2 的整数次幂,但保证 $p-1$ 是 $k$ 的倍数,所以要求原根 $g$.
然后单位根就是 $g^{\frac{(p-1)}{k}}$.
根据费马小定理:$a^{p-1} \equiv 1(\mod p)$,$n$ 次单位根恰好为 $1$,符合要求.
求原根的话直接枚举原根,然后判断是否满足 $g^{\frac{p-1}{prime[i]}}$ 都不为 1 即可.
#include <vector> #include <cmath> #include <cstdio> #include <cstring> #include <algorithm> #define N 100009 #define ll long long #define pb push_back #define db long double #define setIO(s) freopen(s".in","r",stdin) using namespace std; const db pi=acos(-1.0); vector<int>g; int gn,n,L,S,T,P,K,wi[N],val[5][5],seq[N<<1],os[N<<1]; int qpow(int x,int y,int mod) { int tmp=1; for(;y;y>>=1,x=(ll)x*x%mod) { if(y&1) tmp=(ll)tmp*x%mod; } return tmp; } int getroot(int x) { int phi=x-1; for(int i=2;i*i<=x;++i) { if(phi%i==0) { g.pb(i); while(phi%i==0) phi/=i; } } if(phi>1) g.pb(phi); phi=x-1; for(int i=2;;++i) { int flag=1; for(int j=0;j<g.size()&&flag;++j) { if(qpow(i,phi/g[j],x)==1) flag=0; } if(flag) return i; } } ll sqr(ll x) { return x*(x-1)/2; } void init() { gn=qpow(getroot(P),(P-1)/K,P); wi[0]=1; for(int i=1;i<K;++i) { wi[i]=(ll)wi[i-1]*gn%P; } } struct Matrix { int c[3][3]; Matrix(int x=0) { memset(c,0,sizeof(c)); for(int i=0;i<3;++i) c[i][i]=x; } int *operator[](int x){ return c[x]; } Matrix operator*(const Matrix &b) const { Matrix an; for(int i=0;i<3;++i) for(int j=0;j<3;++j) { for(int k=0;k<3;++k) (an.c[i][j]+=(ll)c[i][k]*b.c[k][j]%P)%=P; } return an; } friend Matrix operator^(Matrix x,int y) { Matrix an(1); for(;y;y>>=1,x=x*x) if(y&1) an=an*x; return an; } }W; const int base=1<<15; struct Poly { struct cp { db x,y; cp(db a=0,db b=0) { x=a,y=b; } cp operator+(const cp &b) const { return cp(x+b.x,y+b.y); } cp operator-(const cp &b) const { return cp(x-b.x,y-b.y); } cp operator*(const cp &b) const { return cp(x*b.x-y*b.y,x*b.y+y*b.x); } }f[2][N<<2],g[2][N<<2],ans[3][N<<2]; void FFT(cp *a,int len,int op) { for(int i=0,k=0;i<len;++i) { if(i>k) swap(a[i],a[k]); for(int j=len>>1;(k^=j)<j;j>>=1); } for(int l=1;l<len;l<<=1) { cp wn(cos(pi/l),op*sin(pi/l)); for(int i=0;i<len;i+=l<<1) { cp w(1,0),x,y; for(int j=0;j<l;++j) { x=a[i+j],y=w*a[i+j+l]; a[i+j]=x+y,a[i+j+l]=x-y; w=w*wn; } } } if(op==-1) { for(int i=0;i<len;++i) a[i].x/=len; } } ll actual(db x,int mod) { return (ll)((ll)(x+0.5))%mod; } void MTT(int *a,int n,int *b,int m,int mod,int *c) { int lim; for(lim=1;lim<(n+m+1);lim<<=1); for(int i=0;i<lim;++i) { f[0][i]=f[1][i]=g[0][i]=g[1][i]=cp(); ans[0][i]=ans[1][i]=ans[2][i]=cp(); } for(int i=0;i<=n;++i) { f[0][i].x=a[i]/base; f[1][i].x=a[i]%base; } for(int i=0;i<=m;++i) { g[0][i].x=b[i]/base; g[1][i].x=b[i]%base; } FFT(f[0],lim,1),FFT(f[1],lim,1); FFT(g[0],lim,1),FFT(g[1],lim,1); for(int i=0;i<lim;++i) { ans[0][i]=f[0][i]*g[0][i]; ans[1][i]=f[0][i]*g[1][i]+f[1][i]*g[0][i]; ans[2][i]=f[1][i]*g[1][i]; } for(int i=0;i<3;++i) { FFT(ans[i],lim,-1); } for(int i=0;i<=n+m;++i) { ll x=(ll)(actual(ans[0][i].x,mod)<<30ll)%mod; ll y=(ll)(actual(ans[1][i].x,mod)<<15ll)%mod; ll z=actual(ans[2][i].x,mod); c[i]=(ll)((ll)(x+y)%mod+z)%mod; } } }poly; void get_matrix(int x) { for(int i=0;i<3;++i) { for(int j=0;j<3;++j) { W[i][j]=(ll)val[i+1][j+1]*wi[x]%P; } ++W[i][i]; } } int AA[N<<1],ANS[N*3]; int main() { // setIO("input"); scanf("%d%d%d%d%d%d",&n,&K,&L,&S,&T,&P); init(); for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) scanf("%d",&val[i][j]); } for(int i=0;i<K;++i) { get_matrix(i); W=W^L; seq[i]=(ll)W[S-1][T-1]*wi[(int)((ll)sqr(i)%K)]%P; } for(int i=0;i<=2*K-2;++i) { os[i]=(ll)wi[(int)((ll)(K-(ll)sqr(i)%K+K)%K)]; } for(int i=0;i<K;++i) AA[i]=seq[K-1-i]; poly.MTT(AA,K-1,os,2*K-2,P,ANS); int inv=qpow(K,P-2,P); for(int i=0;i<K;++i) { printf("%d\n",(ll)inv*wi[(int)((ll)sqr(i)%K)]%P*ANS[K-1+i]%P); } return 0; }