传送门
题解:
完全自己推出来的第一道数学神题。
首先我们知道宽度为 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=l∑r(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=l∑r(kfn)n=l∑rk!fnkk!1n=l∑rsk,i(−1)k−in=l∑r(Ax1n+Bx2n)ik!1n=l∑rsk,i(−1)k−ij=0∑i(ji)AjBi−jn=l∑r(x1jx2i−j)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=3⋅gn−1+2⋅i=0∑n−2gi
写出 g n − 1 g_{n-1} gn−1的表达式,差分得到 g n = 4 g n − 1 − g n − 2 g_n=4g_{n-1}-g_{n-2} gn=4gn−1−gn−2
这个形式非常简洁,直接解特征方程可以得到 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+63−3(2−3)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;
}