[BJOI2019]勘破神机

前置技能——求解递推式的通项公式

以二阶递推式 f n = a f n − 1 + b f n − 2 f_n=af_{n-1}+bf_{n-2} fn=afn1+bfn2 为例,做法如下(不会证明):

  • 解出特征方程 x 2 = a x + b x^2=ax+b x2=ax+b 的两个根 x 1 , x 2 x_1,x_2 x1,x2
  • f n = k 1 x 1 n + k 2 x 2 n f_n=k_1x_1^n+k_2x_2^n fn=k1x1n+k2x2n ,带入 f 0 = 1 , f 1 = 1 f_0=1,f_1=1 f0=1,f1=1 解出 k 1 , k 2 k_1,k_2 k1,k2,得到递推式。

本题解法

相当于求 ∑ i = 1 n ( f i K ) \sum_{i=1}^n {f_i\choose K} i=1n(Kfi),把组合数用斯特林反演转化成求 ∑ i = 1 n f i K \sum_{i=1}^n f_i^K i=1nfiK,然后利用通项公式和等比数列求和化简。注意通项公式中有根号,底数 a 可能不是 mod 的二次剩余,因此需要扩域,就是把一个数表示成 x + y a x+y\sqrt a x+ya

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define fir first
#define sec second
#define int ll
using namespace std;
const ll N=510,mod=998244353;
struct Gen {
    ll x,y;
    Gen (ll _x=0,ll _y=0) {x=_x,y=_y;}
};
ll O,S[N][N],C[N][N],fac[N];

inline int mns(int a,int b) {return ((a-b)%mod+mod)%mod;}
inline int add(int a,int b) {return (a+b)%mod;}
Gen operator * (Gen a,Gen b) {return Gen((1ll*a.x*b.x+1ll*a.y*b.y%mod*O%mod)%mod,(1ll*a.x*b.y+1ll*a.y*b.x)%mod);}
Gen operator * (Gen a,ll b) {return Gen(a.x*b%mod,a.y*b%mod);}
Gen operator + (Gen a,Gen b) {return Gen(add(a.x%mod,b.x%mod),add(a.y,b.y));}
bool operator == (Gen a,Gen b) {return a.x==b.x&&a.y==b.y;}

Gen qpow(Gen a,ll b)
{
    Gen ans(1,0),base=a;
    while(b)
    {
        if(b&1) ans=ans*base;
        base=base*base;
        b>>=1;
    }
    return ans;
}
ll qpow(ll a,ll b)
{
    ll ans=1,base=a;
    while(b)
    {
        if(b&1) ans=ans*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return (ans%mod+mod)%mod;
}

Gen operator - (Gen a,Gen b) {return Gen(mns(a.x,b.x),mns(a.y,b.y));}
Gen operator / (Gen a,Gen b) {return a*Gen(b.x,(mod-b.y+mod)%mod)*qpow(((1ll*b.x*b.x-1ll*b.y*b.y%mod*O%mod)%mod+mod)%mod,mod-2);}

ll read()
{
    ll x=0,flag=1;char c=getchar();
    while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
    return x*flag;
}
signed main()
{
    int T=read(),m=read(); Gen k1,k2,x1,x2;
    if(m==2)
    {
        ll t=qpow(2,mod-2),s=qpow(10,mod-2);
        O=5,x1=Gen(t,t),x2=Gen(t,mod-t),k1=Gen(t,s),k2=Gen(t,mod-s);
    }
    else 
    {
        ll t=qpow(2,mod-2),s=qpow(6,mod-2);
        O=3,x1=Gen(2,1),x2=Gen(2,mod-1),k1=Gen(t,s),k2=Gen(t,mod-s);
    }
    fac[0]=1; for(ll i=1;i<=502;i++) fac[i]=1ll*fac[i-1]*i%mod;
    S[0][0]=1; for(ll i=1;i<=502;i++) for(ll j=1;j<=i;j++) S[i][j]=(S[i-1][j-1]-1ll*(i-1)*S[i-1][j])%mod;
    for(int i=0;i<=502;i++) for(ll j=C[i][0]=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;


    while(T--)
    {
        ll l,r,ksj=0; Gen ans(0,0); cin>>l>>r;ll k=read(); ll pl=l,pr=r;
        if(m==3) l=l+1>>1,r=r/2; 
        for(ll j=0;j<=k;j++) 
        {
            Gen t(0,0);
            for(ll e=0;e<=j;e++) 
            {
                Gen ss=qpow(x1,e)*qpow(x2,j-e),z;
                if(ss==Gen(1,0)) z=qpow(k1,e)*qpow(k2,j-e)*qpow(ss,l)*((r-l+1)%mod);
                else z=qpow(k1,e)*qpow(k2,j-e)*qpow(ss,l)*(Gen(1,0)-qpow(ss,r-l+1))/(Gen(1,0)-ss);
                t=t+Gen(C[j][e],0)*z;
            }
            ans=ans+t*S[k][j];
        }
        ksj=ans.x%mod;
        ksj=ksj*qpow(fac[k],mod-2)%mod*qpow((pr-pl+1)%mod,mod-2)%mod; 
        cout<<(ksj%mod+mod)%mod<<'\n';
    }
    return 0;
}
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值