【BJOI2019】勘破神机(下降幂转自然幂)(第一类斯特林数)(特征方程)

传送门


题解:

完全自己推出来的第一道数学神题。

首先我们知道宽度为 2 2 2的部分方案数是斐波那契数列。

f n f_n fn表示长度为 n n n的时候方案数,题目要求的实际上是这个东西:

∑ n = l r ( f n k ) \sum_{n=l}^r{f_n\choose k} n=lr(kfn)

看上去非常地不可做,考虑表示成下降幂的形式,然后利用第一类斯特林数直接转成自然幂,直接上通项公式,然后利用二项式定理展开,后面就是一个等比数列求和:

∑ n = l r ( f n k ) = ∑ n = l r f n k ‾ k ! = 1 k ! ∑ n = l r s k , i ( − 1 ) k − i ∑ n = l r ( A x 1 n + B x 2 n ) i = 1 k ! ∑ n = l r s k , i ( − 1 ) k − i ∑ j = 0 i ( i j ) A j B i − j ∑ n = l r ( x 1 j x 2 i − j ) n \begin{aligned} &\sum_{n=l}^r{f_n\choose k}\\ =&\sum_{n=l}^r\frac{f_n^{\underline{k}}}{k!}\\ =&\frac{1}{k!}\sum_{n=l}^rs_{k,i}(-1)^{k-i}\sum_{n=l}^r(Ax_1^n+Bx_2^n)^i\\ =&\frac{1}{k!}\sum_{n=l}^rs_{k,i}(-1)^{k-i}\sum_{j=0}^i{i\choose j}A^jB^{i-j}\sum_{n=l}^{r}(x_1^jx_2^{i-j})^n \end{aligned} ===n=lr(kfn)n=lrk!fnkk!1n=lrsk,i(1)kin=lr(Ax1n+Bx2n)ik!1n=lrsk,i(1)kij=0i(ji)AjBijn=lr(x1jx2ij)n

由于 k k k很小,直接做就行了。

所以现在的问题就是求通项公式。

宽度为 2 2 2的情况已经玩烂了,就是斐波那契数列。

考虑 3 3 3的情况,显然长度为奇数的情况方案为 0 0 0,设 g n g_n gn表示长度为 2 n 2n 2n的时候的方案数。定义一个块不可分割当且仅当我们无法竖着切一刀且不切到任何一张牌。

注意到宽度为 3 3 3的时候,任意偶数长度的不可切割块都存在,长度为 2 2 2的有 3 3 3种构造方式,长度更长的有 2 2 2种构造方式。

我们有初始条件 g 0 = 1 , g 1 = 3 g_0=1,g_1=3 g0=1,g1=3,以及递推关系:

g n = 3 ⋅ g n − 1 + 2 ⋅ ∑ i = 0 n − 2 g i g_n=3\cdot g_{n-1}+2\cdot \sum_{i=0}^{n-2}g_i gn=3gn1+2i=0n2gi

写出 g n − 1 g_{n-1} gn1的表达式,差分得到 g n = 4 g n − 1 − g n − 2 g_n=4g_{n-1}-g_{n-2} gn=4gn1gn2

这个形式非常简洁,直接解特征方程可以得到 g n = 3 + 3 6 ( 2 + 3 ) n + 3 − 3 6 ( 2 − 3 ) n g_n=\frac{3+\sqrt 3}{6}(2+\sqrt 3)^n+\frac{3-\sqrt 3}{6}(2-\sqrt 3)^n gn=63+3 (2+3 )n+633 (23 )n


代码(两个namespace 是直接复制粘贴的):

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=998244353;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline void ex_gcd(int a,int b,int &x,int &y){
	if(!b){x=1,y=0;return ;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}
inline int inv(int a){
	int x,y;ex_gcd(a,mod,x,y);
	return dec(x,0);
}

cs int N=5e2+7;
int fac[N];
int C[N][N],s[N][N];
inline void init(){
	fac[0]=fac[1]=1;s[0][0]=1;
	for(int re i=2;i<N;++i)fac[i]=mul(fac[i-1],i);
	for(int re i=0;i<N;++i){C[i][0]=1;
		for(int re j=1;j<=i;++j){
			C[i][j]=add(C[i-1][j],C[i-1][j-1]);
			s[i][j]=dec(s[i-1][j-1],mul(s[i-1][j],i-1));
		}
	}
}
//x_n = A * x_1^n + B * x_2^n
namespace Solve2{
	struct cp{
		int x,y;//x + y\sqrt 5
		cp(){}cp(int _x,int _y):x(_x),y(_y){}
		friend cp operator+(cs cp &a,cs cp &b){return cp(add(a.x,b.x),add(a.y,b.y));}
		friend cp operator-(cs cp &a,cs cp &b){return cp(dec(a.x,b.x),dec(a.y,b.y));}
		friend cp operator*(cs cp &a,cs cp &b){
			return cp(add(mul(a.x,b.x),mul(5,mul(a.y,b.y))),add(mul(a.x,b.y),mul(a.y,b.x)));
		}
		friend cp operator+(cs cp &a,int b){return cp(add(a.x,b),a.y);}
		friend cp operator-(cs cp &a,int b){return cp(dec(a.x,b),a.y);}
		friend cp operator*(cs cp &a,int b){return cp(mul(a.x,b),mul(a.y,b));}
		void operator+=(cs cp &b){Inc(x,b.x),Inc(y,b.y);}
	};
	inline cp inv(cs cp &a){return cp(a.x,dec(0,a.y))*::inv(dec(mul(a.x,a.x),mul(5,mul(a.y,a.y))));}
	inline cp power(cp a,ll b){
		cp res(1,0);
		for(;b;b>>=1,a=a*a)if(b&1)res=res*a;
		return res;
	}
	cs cp A=cp(0,::inv(5)),B=cp(0,mod-::inv(5)),x_1=cp(1,1)*::inv(2),x_2=cp(1,mod-1)*::inv(2);
	cp pw_A[N],pw_B[N],pw_1[N],pw_2[N];
	inline void init_pw(){
		pw_A[0]=pw_B[0]=pw_1[0]=pw_2[0]=cp(1,0);
		for(int re i=1;i<N;++i){
			pw_A[i]=pw_A[i-1]*A;
			pw_B[i]=pw_B[i-1]*B;
			pw_1[i]=pw_1[i-1]*x_1;
			pw_2[i]=pw_2[i-1]*x_2;
		}
	}
	inline cp calc(cp a,ll n){
		if(a.x==1&&a.y==0)return cp((n+1)%mod,0);
		return (power(a,n+1)-1)*inv(a-1);
	}
	inline int solve(ll l,ll r,int k){
		cp res(0,0);
		for(int re i=0;i<=k;++i){
			for(int re j=0;j<=i;++j){
				res+=pw_A[j]*pw_B[i-j]*mul(C[i][j],s[k][i])*
				(calc(pw_1[j]*pw_2[i-j],r)-calc(pw_1[j]*pw_2[i-j],l-1));
			}
		}
		return mul(res.x,::inv(fac[k]));
	}
	inline void main(int T){
		init_pw();while(T--){
			ll l,r;int k;scanf("%lld%lld%d",&l,&r,&k);
			cout<<mul(::inv((r-l+1)%mod),solve(l+1,r+1,k))<<"\n";
		}
	}
}

namespace Solve3{
	struct cp{
		int x,y;//x + y\sqrt 3
		cp(){}cp(int _x,int _y):x(_x),y(_y){}
		friend cp operator+(cs cp &a,cs cp &b){return cp(add(a.x,b.x),add(a.y,b.y));}
		friend cp operator-(cs cp &a,cs cp &b){return cp(dec(a.x,b.x),dec(a.y,b.y));}
		friend cp operator*(cs cp &a,cs cp &b){
			return cp(add(mul(a.x,b.x),mul(3,mul(a.y,b.y))),add(mul(a.x,b.y),mul(a.y,b.x)));
		}
		friend cp operator+(cs cp &a,int b){return cp(add(a.x,b),a.y);}
		friend cp operator-(cs cp &a,int b){return cp(dec(a.x,b),a.y);}
		friend cp operator*(cs cp &a,int b){return cp(mul(a.x,b),mul(a.y,b));}
		void operator+=(cs cp &b){Inc(x,b.x),Inc(y,b.y);}
	};
	inline cp inv(cs cp &a){return cp(a.x,dec(0,a.y))*::inv(dec(mul(a.x,a.x),mul(3,mul(a.y,a.y))));}
	inline cp power(cp a,ll b){
		cp res(1,0);
		for(;b;b>>=1,a=a*a)if(b&1)res=res*a;
		return res;
	}
	cs cp A=cp(3,1)*::inv(6),B=cp(3,mod-1)*::inv(6),x_1=cp(2,1),x_2=cp(2,mod-1);
	cp pw_A[N],pw_B[N],pw_1[N],pw_2[N];
	inline void init_pw(){
		pw_A[0]=pw_B[0]=pw_1[0]=pw_2[0]=cp(1,0);
		for(int re i=1;i<N;++i){
			pw_A[i]=pw_A[i-1]*A;
			pw_B[i]=pw_B[i-1]*B;
			pw_1[i]=pw_1[i-1]*x_1;
			pw_2[i]=pw_2[i-1]*x_2;
		}
	}
	inline cp calc(cp a,ll n){
		if(a.x==1&&a.y==0)return cp((n+1)%mod,0);
		return (power(a,n+1)-1)*inv(a-1);
	}
	inline int solve(ll l,ll r,int k){
		cp res(0,0);
		for(int re i=0;i<=k;++i){
			for(int re j=0;j<=i;++j){
				res+=pw_A[j]*pw_B[i-j]*mul(C[i][j],s[k][i])*
				(calc(pw_1[j]*pw_2[i-j],r)-calc(pw_1[j]*pw_2[i-j],l-1));
			}
		}
		return mul(res.x,::inv(fac[k]));
	}
	inline void main(int T){
		init_pw();while(T--){
			ll l,r;int k;scanf("%lld%lld%d",&l,&r,&k);
			cout<<mul(::inv((r-l+1)%mod),solve(l+1>>1,r>>1,k))<<"\n";
		}
	}
}

signed main(){
#ifdef zxyoi
	freopen("calc.in","r",stdin);
#endif
	int T,m;scanf("%d%d",&T,&m);init();
	if(m==2)Solve2::main(T);else Solve3::main(T);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值