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;
}

  

posted @ 2020-07-28 14:09  EM-LGH  阅读(177)  评论(0编辑  收藏  举报