「BJOI 2019」勘破神机

传送门


problem

经过了一个月的艰苦尝试,你的研究团队破译了 “ 2 ” “2” 2 型奥术宝石和 “ 3 ” “3” 3 型奥术宝石的内部能量结构。

这两类结构有着一定的相似性,它们的内部具有 k k k 个反应核心, “ 2 ” “2” 2 型奥术宝石的每个核心都可以看成是一个 2 × n 2×n 2×n 的网格,而 “ 3 ” “3” 3 型奥术宝石的每个核心都可以看成是一个 3 × n 3×n 3×n 的网格。(注意奥术宝石的 k k k n n n 可能不同)当神力反应进行时,每个核心自动填充满神力颗粒。

形式化地描述,每个神力颗粒可以看成是一个 1 × 2 1×2 1×2 横置或竖置的方格,核心填满的定义为每个网格都恰好被一某个方格覆盖。若在两种填满反应核心的方案中存在一个方格放置的位置或方式不同,就认为方案不同。

如填满 2 × 4 2×4 2×4 的网格有 5 5 5 种不同的方案,填满 3 × 2 3×2 3×2 的网格有 3 3 3 种不同的方案。
「BJOI 2019」勘破神机_第1张图片
如果奥术宝石的 k k k 个核心的填充方式互不相同,它们就会组合出强大的咒术。黑袍想知道对于某个宝石一共有多少种不同的咒术(对于两种咒术组合,如果第一种咒术中每个核心 a a a 的填充方式都可以找到第二种咒术的某个核心 b b b,使得 a a a b b b 的填充方式完全相同,则认为这两种咒术组合相同)。

对于宽度为 n n n 、反应核心个数为 k k k “ 2 ” “2” 2 型奥术宝石,设不同的咒术为 F ( n , k ) F(n,k) F(n,k) ;对于宽度为 n n n 、反应核心个数为 k k k “ 3 ” “3” 3 型奥术宝石,设不同的咒术为 G ( n , k ) G(n,k) G(n,k)

地灾军团的科技水平还不能精准测量反应核心的长度 n n n,只能确定出核心长度的大致范围 [ l , r ] [l,r] [l,r]。你需要计算出反应核心长度在此区间内的平均咒术数,即

a n s 2 = 1 r − l + 1 ∑ n = l r F ( n , k ) ans_2=\frac1{r−l+1}\sum _{n=l}^rF(n,k) ans2=rl+11n=lrF(n,k)

a n s 3 = 1 r − l + 1 ∑ n = l r G ( n , k ) ans_3=\frac1{r−l+1}\sum _{n=l}^rG(n,k) ans3=rl+11n=lrG(n,k)

数据范围: 1 ≤ l ≤ r ≤ 1 0 18 1≤l≤r≤10^{18} 1lr1018 1 ≤ k ≤ 501 1≤k≤501 1k501


solution

m = 2 m=2 m=2 的情况

f i f_i fi 表示斐波那契数列( f 1 = 1 , f 2 = 1 f_1=1,f_2=1 f1=1,f2=1),那么用 1 × 2 1\times 2 1×2 的砖铺满 2 × n 2\times n 2×n 的格子的方案数是 f n + 1 f_{n+1} fn+1(为了方便,我们一开始就令 l , r l,r l,r 1 1 1,这样答案就是 f n f_n fn),所以我们要求的实际上就是 ∑ n = l r ( f n k ) \sum_{n=l}^r\binom{f_n}{k} n=lr(kfn)

直接带有斐波那契的话好像不好做,我们考虑斐波那契数列的通项公式为:

f n = 1 5 ( 1 + 5 2 ) n − 1 5 ( 1 − 5 2 ) n f_n=\frac1{\sqrt5}\left(\frac{1+\sqrt5}{2}\right)^n-\frac1{\sqrt5}\left(\frac{1-\sqrt5}{2}\right)^n fn=5 1(21+5 )n5 1(215 )n

那不妨设 A = 1 5 , B = − 1 5 , x = 1 + 5 2 , y = 1 − 5 2 A=\frac1{\sqrt5},B=-\frac1{\sqrt5},x=\frac{1+\sqrt5}{2},y=\frac{1-\sqrt5}{2} A=5 1,B=5 1,x=21+5 ,y=215 ,那么 f n = A x n + B y n f_n=Ax^n+By^n fn=Axn+Byn

这个先留着,待会用于把 f n f_n fn 拆开。

在学习第一类斯特林数时,我们貌似学习过这个公式:

x n ‾ = ∑ i = 0 n ( − 1 ) n − i [ n i ] x i x^{\underline n}=\sum_{i=0}^n(-1)^{n-i}\begin{bmatrix}n\\i\end{bmatrix}x^i xn=i=0n(1)ni[ni]xi

其中 [ n i ] \begin{bmatrix}n \\i\end{bmatrix} [ni] 为第一类斯特林数。证明就在这里看啦。

又根据 ( n m ) = n m ‾ m ! \binom n m=\frac{n^{\underline m}}{m!} (mn)=m!nm,我们继续化简:

∑ n = l r ( f n k ) = 1 k ! ∑ n = l r ∑ i = 0 k ( − 1 ) k − i [ k i ] f n i = 1 k ! ∑ n = l r ∑ i = 0 k ( − 1 ) k − i [ k i ] ( A x n + B y n ) i = 1 k ! ∑ n = l r ∑ i = 0 k ( − 1 ) k − i [ k i ] ∑ j = 0 i ( i j ) A j B i − j ( x j y i − j ) n = 1 k ! ∑ i = 0 k ( − 1 ) k − i [ k i ] ∑ j = 0 i ( i j ) A j B i − j ∑ n = l r ( x j y i − j ) n \begin{aligned}\sum_{n=l}^r{f_n\choose k}&=\frac 1 {k!}\sum_{n=l}^r\sum_{i=0}^k(-1)^{k-i}\begin{bmatrix}k\\i\end{bmatrix}f_n^i\\&=\frac1{k!}\sum_{n=l}^r\sum_{i=0}^k(-1)^{k-i}\begin{bmatrix}k\\i\end{bmatrix}(Ax^n+By^n)^i\\&=\frac1{k!}\sum_{n=l}^r\sum_{i=0}^k(-1)^{k-i}\begin{bmatrix}k\\i\end{bmatrix}\sum_{j=0}^i{i\choose j}A^jB^{i-j}(x^jy^{i-j})^n\\&=\frac1{k!}\sum_{i=0}^k(-1)^{k-i}\begin{bmatrix}k\\i\end{bmatrix}\sum_{j=0}^i{i\choose j}A^jB^{i-j}\sum_{n=l}^r(x^jy^{i-j})^n\\\end{aligned} n=lr(kfn)=k!1n=lri=0k(1)ki[ki]fni=k!1n=lri=0k(1)ki[ki](Axn+Byn)i=k!1n=lri=0k(1)ki[ki]j=0i(ji)AjBij(xjyij)n=k!1i=0k(1)ki[ki]j=0i(ji)AjBijn=lr(xjyij)n

最后面的那个 ∑ n = l r ( x j y i − j ) n \sum_{n=l}^r(x^jy^{i-j})^n n=lr(xjyij)n 是一个等比数列求和,要注意有公比 = 1 =1 =1 的情况。

那我们就可以枚举 i , j i,j i,j 然后 O ( k 2 log ⁡ n ) O(k^2\log n) O(k2logn) 算答案了。

还有一个细节是,就是 5 \sqrt 5 5 在模 998244353 998244353 998244353 意义下是不存在的,那么我们类似于表示虚数的方法,把每个数表示成 a + b 5 a+b\sqrt 5 a+b5 就可以了。

m = 3 m=3 m=3 的情况

以下内容部分来自 ViXBob 的博客,实在不想写写不动了。

先考虑求出递推式,比较显然的是当 n n n 是奇数时答案是 0 0 0,所以设 g n g_n gn 表示 3 × 2 n 3\times 2n 3×2n 的答案。

如下图:
「BJOI 2019」勘破神机_第2张图片
枚举一下第二种情况跨越红线的数量就有递推式:

g n = 3 × g n − 1 + 2 × ∑ i = 1 n − 1 g n − 1 − i = 3 × g n − 1 + 2 × ∑ i = 0 n − 2 g i \begin{aligned}g_n&=3\times g_{n-1}+2\times\sum_{i=1}^{n-1}g_{n-1-i}\\&=3 \times g_{n-1}+2\times\sum_{i=0}^{n-2}g_i\end{aligned} gn=3×gn1+2×i=1n1gn1i=3×gn1+2×i=0n2gi

则有:

{ g n = 3 × g n − 1 + 2 × ∑ i = 0 n − 2 g i g n + 1 = 3 × g n + 2 × g n − 1 + 2 × ∑ i = 0 n − 2 g i \begin{cases}g_n=3 \times g_{n-1}+2\times\sum_{i=0}^{n-2}g_i\\g_{n+1}=3\times g_n+2\times g_{n-1}+2\times \sum_{i=0}^{n-2}g_i\end{cases} {gn=3×gn1+2×i=0n2gign+1=3×gn+2×gn1+2×i=0n2gi

差分一下:

g n + 1 = 4 × g n − g n − 1 g_{n+1}=4 \times g_n-g_{n-1} gn+1=4×gngn1

也就是说 g n g_n gn 的递推式为:

{ g n = 1 , n = 0 g n = 3 , n = 1 g n = 4 × g n − 1 − g n − 2 , n > 1 \begin{cases}g_n=1,&n=0\\g_n=3,&n=1\\g_n=4 \times g_{n-1}-g_{n-2},&n>1\end{cases} gn=1,gn=3,gn=4×gn1gn2,n=0n=1n>1

用特征方程方程解得:

g n = 3 + 3 6 ( 2 + 3 ) n + 3 − 3 6 ( 2 − 3 ) n g_n=\frac{3+\sqrt3}{6}(2+\sqrt3)^n+\frac{3-\sqrt3}{6}(2-\sqrt3)^n gn=63+3 (2+3 )n+633 (23 )n

然后就是一模一样的求解方法了。

时间复杂度 O ( k 2 log ⁡ n ) O(k^2\log n) O(k2logn)


code

#include
#define ll long long
using namespace std;
const int N=505,P=998244353,inv2=499122177,inv6=166374059;
int T,m,k,V,s[N][N],fac[N],ifac[N];
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
int power(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
struct num{
	int x,y;
	num(int _x=0,int _y=0):x(_x),y(_y){}
	friend num operator+(const num &a,const num &b)  {return num(add(a.x,b.x),add(a.y,b.y));}
	friend num operator-(const num &a,const num &b)  {return num(dec(a.x,b.x),dec(a.y,b.y));}
	friend num operator-(const num &a,int b)  {return num(dec(a.x,b),a.y);}
	friend num operator*(const num &a,int b)  {return num(mul(a.x,b),mul(a.y,b));}
	friend num operator*(const num &a,const num &b){
		return num(add(mul(a.x,b.x),1ll*V*mul(a.y,b.y)%P),add(mul(a.x,b.y),mul(a.y,b.x)));
	}
	friend num Inv(const num &a){
		return num(a.x,P-a.y)*power(dec(mul(a.x,a.x),1ll*V*mul(a.y,a.y)%P),P-2);
	}
	friend num power(num a,ll b){
		num ans=num(1,0);
		for(;b;b>>=1,a=a*a)  if(b&1)  ans=ans*a;
		return ans;
	}
}A,B,X,Y;
void prework(){
	fac[0]=fac[1]=1;
	for(int i=2;i<N;++i)  fac[i]=mul(fac[i-1],i);
	ifac[N-1]=power(fac[N-1],P-2);
	for(int i=N-2;~i;--i)  ifac[i]=mul(ifac[i+1],i+1);
	s[0][0]=1;
	for(int i=1;i<N;++i)
		for(int j=1;j<N;++j)
			s[i][j]=add(s[i-1][j-1],mul(i-1,s[i-1][j]));
}
int C(int n,int m)  {return n<m?0:mul(fac[n],mul(ifac[m],ifac[n-m]));}
void init(){
	if(m==2){
		V=5;
		X=num(inv2,inv2),Y=num(inv2,P-inv2),A=Inv(num(0,1)),B=Inv(num(0,P-1));
	}
	else{
		V=3;
		X=num(2,1),Y=num(2,P-1),A=num(inv2,inv6),B=num(inv2,P-inv6);
	}
}
ll l,r,L,R;
int solve(){
	int ans=0;
	for(int i=0;i<=k;++i){
		int res=0,tmp=((k-i)&1)?(P-s[k][i]):s[k][i];
		for(int j=0;j<=i;++j){
			num val1=power(A,j)*power(B,i-j)*C(i,j),val2=power(X,j)*power(Y,i-j);
			val2=(val2.x==1&&val2.y==0)?num((r-l+1)%P,0):(power(val2,r+1)-power(val2,l))*Inv(val2-1);
			res=add(res,(val1*val2).x);
		}
		ans=add(ans,mul(res,tmp));
	}
	return mul(mul(ans,ifac[k]),power((R-L+1)%P,P-2));
}
int main(){
	prework();
	scanf("%d%d",&T,&m),init();
	while(T--){
		scanf("%lld%lld%d",&L,&R,&k);
		(m==2)?(l=L+1,r=R+1):(l=(L+1)/2,r=R/2);
		printf("%d\n",solve());
	}
	return 0;
}

你可能感兴趣的:(#,斯特林数)