[BJOI2019]勘破神机
有点毒瘤的一个题。(主要原因还是我太菜
第一个问题求的大概是一个 sigema n C(F[n],K),其中F[n]是斐波那契数列。
考虑把组合数转成下降幂,用第一类斯特林数展开。
转化为
sigema n sigema i fn^i ✖ S(K,i)✖ (-1)^(K-i)
换一下求和顺序,把S和-1提到外面。
考虑斐波那契数列的通项公式,是一个A✖a^n + B✖b^n 的形式。
这个东西的i次方显然可以二项式定理展开。
继续推一下。
大概是这个东西,最后里面的东西是一个等比数列,可以O(logn)求出,然后加上外层枚举,总复杂度O(K^2 logn)
然而斐波那契数列的通项公式里含有一个无理数,模意义无对应的值。
用一个经典套路,考虑把所有数字表示成一个类似复数的形式,即a+b✖sqrt(k)的形式,推一下它对应的基本运算的公式就可以了。
对于第二问,可以发现F[n]=3✖F[n-2]+2✖F[n-4]+2✖F[n-6]....
奇数项显然没用,全部除以2。
F[n]=3✖F[n-1]+2✖F[n-2]+2✖F[n-3]....
前缀和优化一下
S[n]=S[n-1]+F[n]
F[n]=F[n-1]+2✖S[n-1]
瞎比推一下这些式子。
S[n]=S[n-1]+F[n-1]+2✖S[n-1]=3✖S[n-1]+F[n-1]=4✖S[n-1]-S[n-2]
再相邻项作差
S[n-1]=4✖S[n-2]-S[n-3]
S[n]=4✖S[n-1]-S[n-2]
可得
F[n]=4✖F[n-1]+F[n-2]
然后特征根方程求一下通项就和第一问的做法一样了。
#include<bits/stdc++.h>
#define N 1100
#define L 1000
#define eps 1e-7
#define inf 1e9+7
#define db double
#define ll long long
#define ldb long double
#define ull unsigned long long
using namespace std;
inline ll read()
{
char ch=0;
ll x=0,flag=1;
while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*flag;
}
const ll mo=998244353;
ll ksm(ll x,ll k)
{
k=(k%(mo-1)+(mo-1))%(mo-1);ll ans=1;
while(k){if(k&1)ans=1ll*ans*x%mo;k>>=1;x=1ll*x*x%mo;}
return ans;
}
ll inv(ll x){return ksm((x%mo+mo)%mo,mo-2);}
ll test,flag,kv,fac[N],C[N][N],S[N][N];
struct node{ll x,y;};
node inv(node o)
{
o.x=(o.x%mo+mo)%mo;o.y=(o.y%mo+mo)%mo;
ll a=o.x,b=o.y,k=((1ll*a%mo*a%mo)-(kv*b%mo*b%mo))%mo;
return {1ll*a*inv(+k)%mo,1ll*b*inv(-k)%mo};
}
node operator+(ll a,node b){return {(a+b.x)%mo,b.y};}
node operator+(node a,ll b){return {(a.x+b)%mo,a.y};}
node operator+(node a,node b){return {(a.x+b.x)%mo,(a.y+b.y)%mo};}
node operator-(ll a,node b){return {(a-b.x)%mo,-b.y};}
node operator-(node a,ll b){return {(a.x-b)%mo,+a.y};}
node operator-(node a,node b){return {(a.x-b.x)%mo,(a.y-b.y)%mo};}
node operator*(ll a,node b){return {1ll*a*b.x%mo,1ll*a*b.y%mo};}
node operator*(node a,ll b){return {1ll*a.x*b%mo,1ll*a.y*b%mo};}
node operator*(node a,node b)
{
return
{
((1ll*a.x%mo*b.x%mo)+(kv*a.y%mo*b.y%mo))%mo,((1ll*a.x*b.y%mo)+(1ll*a.y*b.x%mo))%mo
};
}
node operator/(node a,node b){return a*inv(b);}
node operator^(node x,ll k)
{
node ans={1,0};
while(k){if(k&1)ans=ans*x;k>>=1;x=x*x;}
return ans;
}
void work1()
{
ll l=read()+1,r=read()+1,m=read(),len=r-l+1;
node A=(node){+1,0}/(node){0,1},B=(node){-1,0}/(node){0,1};
node a={inv(2),+inv(2)},b={inv(2),-inv(2)},ans={0,0};
for(ll k=0;k<=m;k++)
{
node tot={0,0};
for(ll i=0;i<=k;i++)
{
node a1=((a^i)*(b^(k-i)))^l,q=(a^i)*(b^(k-i)),o={0,0};
if(((q.x%mo+mo)%mo)==1&&((q.y%mo+mo)%mo)==0)o=(r-l+1)*a1;
else o=a1*((1-(q^(r-l+1)))/(1-q));
tot=tot+(o*C[k][i]*(A^i)*(B^(k-i)));
}
ans=ans+tot*ksm(-1,m-k)*S[m][k];
}
ans=ans*(1ll*inv(fac[m])*inv(len)%mo);
printf("%lld\n",(ans.x%mo+mo)%mo);
}
void work2()
{
ll l=read(),r=read(),m=read(),len=r-l+1;
if(l&1)l++;if(r&1)r--;l/=2;r/=2;l++;r++;
node A=(node){0,+inv(6)},B=(node){0,-inv(6)};
node a={2,+1},b={2,-1},ans={0,0};
for(ll k=0;k<=m;k++)
{
node tot={0,0};
for(ll i=0;i<=k;i++)
{
node t=C[k][i]*(A^i)*(B^(k-i));
node a1=(((a^l)-(a^(l-1)))^i)*(((b^l)-(b^(l-1)))^(k-i)),q=(a^i)*(b^(k-i)),o={0,0};
if(((q.x%mo+mo)%mo)==1&&((q.y%mo+mo)%mo)==0)o=((r-l+1)%mo)*a1;
else o=a1*(((q^(r-l+1))-1)/(q-1));
tot=tot+(o*t);
}
ans=ans+tot*ksm(-1,m-k)*S[m][k];
}
ans=ans*(1ll*inv(fac[m])*inv(len)%mo);
printf("%lld\n",(ans.x%mo+mo)%mo);
}
int main()
{
test=read();flag=read();fac[0]=S[0][0]=1;
if(flag==2)kv=5;else kv=3;
for(ll i=1;i<=L;i++)fac[i]=1ll*fac[i-1]*i%mo;
for(ll i=0;i<=L;i++)
{
C[i][0]=1;
for(ll j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
}
for(ll i=1;i<=L;i++)
{
for(ll j=1;j<=i;j++)S[i][j]=(S[i-1][j-1]+(1ll*S[i-1][j]*(i-1)%mo))%mo;
}
for(ll i=1;i<=test;i++)if(flag==2)work1();else work2();
return 0;
}