[BJOI2019]勘破神机

本文探讨了如何利用斐波那契数列和第一类斯特林数的性质,通过计算C(f_i)^k和C(g_i)^k的组合来解决填充不同网格的方案数问题。通过化简通项公式和等比数列求和,提出了一种O(Tk^2logn)的时间复杂度算法,并给出了C++代码实现。

题目描述

定义fnf_nfn为用1×21\times 21×2骨牌填满2×n2\times n2×n网格的方案数,gng_ngn为填满3×n3\times n3×n网格的方案数。

求:

1r−l+1∑i=lrCfik/1r−l+1∑i=lrCgikmod 998244353\frac{1}{r-l+1}\sum_{i=l}^rC_{f_i}^k/\frac{1}{r-l+1}\sum_{i=l}^rC_{g_i}^k\texttt{mod 998244353}rl+11i=lrCfik/rl+11i=lrCgikmod 998244353

l≤r≤1018,k≤500l\leq r\leq 10^{18},k\leq 500lr1018,k500

解题思路

Cfik=fik‾k!C_{f_i}^k=\frac{f_i^{\underline k}}{k!}Cfik=k!fik

fnk‾=∑i=0k(−1)k−iSkifnif_n^{\underline k}=\sum_{i=0}^k(-1)^{k-i}S_k^if_n^ifnk=i=0k(1)kiSkifni

其中 SnkS_n^kSnk代表第一类斯特林数。
∑i=0nCfik=1k!∑i=0n∑j=0k(−1)k−jSkjfij=1k!∑j=0k(−1)k−jSkj∑i=0nfij \begin{aligned} \sum_{i=0}^nC_{f_i}^k&=\frac{1}{k!}\sum_{i=0}^n\sum_{j=0}^k(-1)^{k-j}S_k^jf_i^j\\ &=\frac{1}{k!}\sum_{j=0}^k(-1)^{k-j}S_k^j\sum_{i=0}^nf_i^j\\ \end{aligned} i=0nCfik=k!1i=0nj=0k(1)kjSkjfij=k!1j=0k(1)kjSkji=0nfij

问题变为求解 ∑i=0nfij\sum_{i=0}^nf_i^ji=0nfij

众所周知 fnf_nfn 是斐波那契第n+1n+1n+1项。

斐波那契通项公式:
fn=15((1+52)n+1−(1−52)n+1)f_n=\frac{1}{\sqrt 5}((\frac{1+\sqrt 5}{2})^{n+1}-(\frac{1-\sqrt 5}{2})^{n+1})fn=51((21+5)n+1(215)n+1)

可以化为fn=Aαn+1+Bβn+1f_n=A\alpha^{n+1}+B\beta^{n+1}fn=Aαn+1+Bβn+1

带入原式并展开:
∑i=0n(Aαi+1+Bβi+1)k=∑i=0n∑j=0kCkj(Aαi+1)j(Bβi+1)k−j=∑i=0n∑j=0kCkjAjα(i+1)jBk−jβ(i+1)(k−j)=∑i=0n∑j=0kCkjAjBk−j(αjβk−j)i+1=∑j=0kCkjAjBk−j∑i=0n(αjβk−j)i+1 \begin{aligned} \sum_{i=0}^n(A\alpha^{i+1}+B\beta^{i+1})^k&=\sum_{i=0}^n\sum_{j=0}^kC_k^j(A\alpha^{i+1})^j(B\beta^{i+1})^{k-j}\\ &=\sum_{i=0}^n\sum_{j=0}^kC_k^jA^j\alpha^{(i+1)j}B^{k-j}\beta^{(i+1)(k-j)}\\ &=\sum_{i=0}^n\sum_{j=0}^kC_k^jA^jB^{k-j}(\alpha^j\beta^{k-j})^{i+1}\\ &=\sum_{j=0}^kC_k^jA^jB^{k-j}\sum_{i=0}^n(\alpha^j\beta^{k-j})^{i+1} \end{aligned} i=0n(Aαi+1+Bβi+1)k=i=0nj=0kCkj(Aαi+1)j(Bβi+1)kj=i=0nj=0kCkjAjα(i+1)jBkjβ(i+1)(kj)=i=0nj=0kCkjAjBkj(αjβkj)i+1=j=0kCkjAjBkji=0n(αjβkj)i+1

前面枚举后直接算,后边是等比数列求和可以O(1)O(1)O(1)算,需要特判公比为111的情况。

因为5\sqrt 55不存在mod 998244353\texttt{mod 998244353}mod 998244353下的二次剩余,因此需要扩域,即把数字表示成a+b5a+b\sqrt 5a+b5的形式,类似复数的运算。

gng_ngn也可以找出通项公式然后表示成和fnf_nfn相同的形式,可以类似算。

复杂度O(Tk2log⁡n)O(Tk^2\log n)O(Tk2logn)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
inline LL Pow(LL a,LL b)
{
	a%=mod;
	LL res=1;
	while(b)
	{
		if(b&1)res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
LL D;
struct Complex
{
	LL a,b;
	Complex(){a=b=0;}
	Complex(LL x,LL y){a=(x%mod+mod)%mod;b=(y%mod+mod)%mod;}
	Complex operator +(Complex x){return Complex(a+x.a,b+x.b);}
	Complex operator -(Complex x){return Complex(a-x.a,b-x.b);}
	Complex operator *(Complex x){return Complex(a*x.a+b*x.b%mod*D,a*x.b+b*x.a);}
};
Complex operator *(LL k,Complex x){return Complex(k*x.a,k*x.b);}
Complex inv(Complex x)
{
	LL P=(x.a*x.a%mod-x.b*x.b%mod*D%mod+mod)%mod;
	P=Pow(P,mod-2);
	return Complex(x.a*P%mod,mod-x.b*P%mod);
}
Complex Pow(Complex a,LL b)
{
	Complex res=Complex(1,0);
	a=Complex(a.a,a.b);
	while(b)
	{
		if(b&1)res=res*a;
		a=a*a;
		b>>=1;
	}
	return res;
}
const int N = 520;
LL S[N][N],C[N][N]; 
LL I2=Pow(2,mod-2),I6=Pow(6,mod-2);
int m;
Complex alpha,beta,A,B;
LL L,R,k;
LL pw(LL x){return (x&1)?mod-1:1;}
LL calc(LL n,LL k)
{
	LL coef=1;
	for(int i=1;i<=k;i++)coef=1ll*coef*i%mod;
	coef=Pow(coef,mod-2);
	Complex One=Complex(1,0),Zero=Complex(0,0);
	LL ans=0;
	for(int i=0;i<=k;i++)
	{
		LL P=1ll*S[k][i]*pw(k-i)%mod;
		Complex res=Zero;
		for(int j=0;j<=i;j++)
		{
			Complex u=Pow(alpha,i-j)*Pow(beta,j);
			Complex v=Pow(u,n+1);
			if(u.a==1&&u.b==0) v.a=(n+1)%mod,v.b=0;
			else u=inv(One-u),v=(One-v)*u;
			v=v*Pow(A,i-j)*Pow(B,j);
			v=C[i][j]*v;
			res=res+v;
		}
		ans=(ans+1ll*P*res.a%mod)%mod;
	}
	
	return 1ll*ans*coef%mod;
}
void solve()
{
	scanf("%lld %lld %lld",&L,&R,&k);
	LL coef=Pow(R-L+1,mod-2);
	if(m==2)R++;
	else L=(L-1)/2,R/=2;
	LL res=(calc(R,k)-calc(L,k)+mod)%mod*coef%mod;
	printf("%lld\n",res);
}
void put(Complex x)
{
	cout<<x.a<<' '<<x.b<<endl;
}
int main()
{
	C[0][0]=1;S[0][0]=1;
	for(int i=1;i<N;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)
		{
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
			S[i][j]=(S[i-1][j-1]+1ll*(i-1)*S[i-1][j]%mod)%mod;
		}
	}
	int T;
	cin>>T>>m;
	if(m==2)
	{
		D=5;
		alpha=Complex(I2,I2);
		beta=Complex(I2,-I2);
		A=Complex(1,0)*inv(Complex(0,1));
		B=Complex(0,0)-A;
	}
	if(m==3)
	{
		D=3;
		alpha=Complex(2,-1);
		beta=Complex(2,1);
		A=Complex(I2,-I6);
		B=Complex(I2,I6);
	}
	while(T--)
	{
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值