[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;
}
posted @ 2019-04-25 01:15  Creed-qwq  阅读(278)  评论(0编辑  收藏  举报