LOJ6696 复读机 加强版

Link
考虑单个元素的EGF,显然为\(f(x)=\sum\limits_{i\ge0}[d|i]\frac{x^i}{i!}=\frac1d\sum\limits_{i=0}^{d-1}\exp(\omega_d^ix)\)
那么答案就是\([x^n]f(x)^k\)
考虑分圆多项式\(\Phi_d(x)=\prod\limits_{k\in[0,d)\wedge\gcd(d,k)=1}(x-\omega_d^k)=\prod\limits_{k|d}(x^{\frac dk}-1)^{\mu(k)}\)
那么\(\omega_d^k=(x^k\bmod\Phi_d(x))\mid_{x=\omega}\)
很显然\(\deg(\Phi_d(x))=\varphi(d)\),且\(\Phi_d(x)\)的系数都是整数,因此\(\omega_d^k\)可以由\(\omega_d^0,\cdots,\omega_d^{\varphi(d)-1}\)以整数线性表示。
实际上可以证明,对于域\(\mathbb N\)上的线性空间\(V=\operatorname{span}(\omega_d^0,\cdots,\omega_d^{d-1})\)一定有\(\dim V=\varphi(d)\)
这样我们就可以把\(f(x)\)写成\(F(\exp x,\exp(\omega x),\cdots,\exp(\omega_d^{d-1}x))\)的形式了。
\(G=F^k\),那么\(F\frac{\partial G}{\partial x}=kG\frac{\partial F}{\partial x}\)
由这个式子列出递推式然后解得系数是trivial的。
时间复杂度为\(O(k^{\varphi(d)}\log n)\)\(\log n\)是快速幂的。
\(d=2\)时可以通过线性筛幂函数做到\(O(k)\)

#include<cstdio>
using i64=long long;
const int N=2007,P=19491001;
int n,k,d;i64 w,ans,fac[N],ifac[N],inv[N];
int abs(int x){return x<0? -x:x;}
void inc(i64&a,i64 b){a+=b-P,a+=a>>63&P;}
i64 pow(i64 a,i64 b){i64 c=1;for(a%=P;b;b>>=1,a=a*a%P)if(b&1)c=c*a%P;return c;}
i64 binom(int n,int m){return fac[n]*ifac[m]%P*ifac[n-m]%P;}
void init(int n=2000)
{
    fac[0]=ifac[0]=inv[0]=fac[1]=ifac[1]=inv[1]=1,w=pow(7,(P-1)/d);
    for(int i=2;i<=n;++i) fac[i]=i*fac[i-1]%P,inv[i]=(P-P/i)*inv[P%i]%P,ifac[i]=inv[i]*ifac[i-1]%P;
}
namespace Sub1
{
    void solve()
    {
	for(int a=0;a<=k;++a) for(int b=2-((k^a)&1);a+b<=k;b+=2) inc(ans,pow(a*w+b,n)*binom(k,(k+a+b)/2)%P*binom(k,(k+abs(a-b))/2)%P);
	printf("%lld",4*pow(d,P-k-1)*ans%P);
    }
}
namespace Sub2
{
    i64 a[N][N],b[N][N];
    void solve()
    {
	for(int i=0;i<=k;++i) a[0][i]=a[i][0]=binom(k,i),b[i][0]=i*a[i][0]%P;
	for(int i=1;i<=k+k;++i)
	    for(int j=1;j<=i&&j<=k;++j)
	    {
		i64 x=k*a[i-1][j]-b[i-1][j]-b[i][j-1];
		if(j>=2) x+=k*a[i-1][j-2]-b[i-1][j-2];
		if(i>=2) x+=2*k*a[i-2][j-1]-b[i-2][j-1];
		if(i>=2&&j>=2) x+=2*k*a[i-2][j-2]-b[i-2][j-2];
		x=(x+4*P)%P,b[i][j]=x,a[i][j]=inv[i]*x%P;
		if(i<=k) a[j][i]=a[i][j],b[j][i]=j*a[j][i]%P;
	    }
	for(int i=0;i<=k+k;++i) for(int j=0;j<=k;++j) inc(ans,(j==k? 1:2)*pow((P+i-k)*w+k-j,n)*a[i][j]%P);
	printf("%lld",ans*pow(d,P-k-1)%P);
    }
}
int main()
{
    scanf("%d%d%d",&n,&k,&d),init();
    if(n%d) puts("0"); else if(d==4) Sub1::solve(); else if(d==6) Sub2::solve();
}
posted @ 2020-05-25 16:31  Shiina_Mashiro  阅读(307)  评论(0编辑  收藏  举报