[BJOI2019]勘破神机(第一类斯特林数,斐波那契数列)

真的是好题,只不过强行多合一有点过分了……

题目大意:

$T$ 组数据。每个测试点中 $m$ 相同。

对于每组数据,给定 $l,r,k$,请求出 $\dfrac{1}{r-l+1}\sum\limits_{n=l}^r\dbinom{f(n,m)}{k}\bmod 998244353$。

其中 $f(n,m)$ 表示用 $1\times 2$ 的骨牌(可以变成 $2\times 1$)填满 $n\times m$ 的网格的方案数。

$1\le T\le 5,1\le l\le r\le 10^{18},1\le k\le 501,2\le m\le 3$。保证 $r-l+1$ 不是 $998244353$ 的倍数。



$2\le m\le 3$,明显二合一了。(其实后面会发现不止二合一)

先看 $m=2$。众所周知 $f(n,2)=fib_{n+1}$。然后就变成这题了。注意 $\sqrt{5}$ 在模 $998244353$ 下也没有意义,还是要扩系。

接下来看 $m=3$。

首先肯定 $n$ 是偶数的时候 $f(n,3)$ 才不为 $0$,那么设 $g_n=f(2n,3)$,然后要求就是 $\sum\limits_{n=\lceil\frac{l}{2}\rceil}^{\lfloor\frac{r}{2}\rfloor}g_n$。(为方便下文假设求 $l$ 到 $r$ 的和)

(从题解偷张图,%%%vixbob

说得应该很清楚了。那么 $g_n=3g_{n-1}+2\sum\limits_{i=0}^{n-2}g_i$。

那么 $g_{n+1}-g_n=3g_n-g_{n-1}$,得递推公式 $g_n=4g_{n-1}-g_{n-2}$。初始 $g_0=1,g_1=3$。

用特征方程解出通项公式:

$$g_n=\dfrac{3+\sqrt{3}}{6}(2+\sqrt{3})^n+\dfrac{3-\sqrt{3}}{6}(2-\sqrt{3})^n$$

然后就一样了。

时间复杂度 $O(Tk^2\log r)$。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=555,mod=998244353,inv2=499122177,inv5=598946612,inv6=166374059;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
    char ch=getchar();ll x=0,f=0;
    while(ch<‘0‘ || ch>‘9‘) f|=ch==‘-‘,ch=getchar();
    while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
    return f?-x:x;
}
int t,m,k,fac[maxn],invfac[maxn],S[maxn][maxn],C[maxn][maxn];
ll l,r;
inline int add(int x,int y){return x+y<mod?x+y:x+y-mod;}
inline int sub(int x,int y){return x<y?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int qpow(int a,ll b){
    int ans=1;
    for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
    return ans;
}
template<int T>
struct comp{
    int x,y;
    comp(const int xx=0,const int yy=0):x(xx),y(yy){}
    inline comp operator+(const comp &c)const{return comp(add(x,c.x),add(y,c.y));}
    inline comp operator-(const comp &c)const{return comp(sub(x,c.x),sub(y,c.y));}
    inline comp operator*(const comp &c)const{return comp(add(mul(x,c.x),mul(T,mul(y,c.y))),add(mul(x,c.y),mul(y,c.x)));}
    inline comp inv()const{
        comp ans(x,y?mod-y:0);
        int dn=qpow(sub(mul(x,x),mul(T,mul(y,y))),mod-2);
        return ans*dn;
    }
    inline comp operator/(const comp &c)const{return *this*c.inv();}
    inline bool operator==(const comp &c)const{return x==c.x && y==c.y;}
};
comp<5> a2(0,inv5),b2(0,mod-inv5),x2(inv2,inv2),y2(inv2,mod-inv2);
comp<3> a3(inv2,inv6),b3(inv2,mod-inv6),x3(2,1),y3(2,mod-1);
template<int T>
inline comp<T> cqpow(comp<T> a,ll b){
    comp<T> ans(1,0);
    for(;b;b>>=1,a=a*a) if(b&1) ans=ans*a;
    return ans;
}
template<int T>
comp<T> calc(comp<T> x,ll l,ll r){
    if(x==1) return (r-l+1)%mod;
    return (cqpow(x,r+1)-cqpow(x,l))/(x-1);
}
int main(){
    FOR(i,0,501) C[i][0]=C[i][i]=1;
    FOR(i,1,501) FOR(j,1,i-1) C[i][j]=add(C[i-1][j],C[i-1][j-1]);
    S[1][1]=1;
    FOR(i,2,501) FOR(j,1,i) S[i][j]=add(mul(i-1,S[i-1][j]),S[i-1][j-1]);
    fac[0]=1;
    FOR(i,1,501) fac[i]=mul(fac[i-1],i);
    invfac[501]=qpow(fac[501],mod-2);
    ROF(i,500,0) invfac[i]=mul(invfac[i+1],i+1);
    t=read();m=read();
    while(t--){
        l=read();r=read();k=read();
        if(m==2){
            int ans=0;
            FOR(i,0,k){
                int s=0;
                FOR(j,0,i){
                    comp<5> tmp1=cqpow(a2,j)*cqpow(b2,i-j),tmp2=cqpow(x2,j)*cqpow(y2,i-j);
                    s=add(s,mul(C[i][j],(tmp1*calc(tmp2,l+1,r+1)).x));
                }
                s=mul(s,S[k][i]);
                if((k-i)&1) ans=sub(ans,s);
                else ans=add(ans,s);
            }
            printf("%d\n",mul(mul(ans,invfac[k]),qpow((r-l+1)%mod,mod-2)));
        }
        else{
            ll lll=(l+1)>>1,rrr=r>>1;
            if(lll>rrr){puts("0");continue;}
            int ans=0;
            FOR(i,0,k){
                int s=0;
                FOR(j,0,i){
                    comp<3> tmp1=cqpow(a3,j)*cqpow(b3,i-j),tmp2=cqpow(x3,j)*cqpow(y3,i-j);
                    s=add(s,mul(C[i][j],(tmp1*calc(tmp2,lll,rrr)).x));
                }
                s=mul(s,S[k][i]);
                if((k-i)&1) ans=sub(ans,s);
                else ans=add(ans,s);
            }
            printf("%d\n",mul(mul(ans,invfac[k]),qpow((r-l+1)%mod,mod-2)));
        }
    }
}

原文地址:https://www.cnblogs.com/1000Suns/p/11031106.html

时间: 2024-11-13 01:32:56

[BJOI2019]勘破神机(第一类斯特林数,斐波那契数列)的相关文章

BJOI2019勘破神机(斯特林数+二项式定理+数学)

题意:f[i],g[i]分别表示用1*2的骨牌铺2*n和3*n网格的方案数,求ΣC(f(i),k)和ΣC(g(i),k),对998244353取模,其中l<=i<=r,1<=l<=r<=1e18 题解:显然打表发现f[i]为斐波那契数列,g[2i+1]=0,g[2i]=4g[2i-2]-g[2i-4]. 然后考虑m=2的斐波那契部分:k是给定的,仅需求斐波那契数列的下降幂,然后可以用第一类斯特林数去转换,然后求斐波那契数列的幂之和,假设斐波那契数列的两个特征根为a,b,则f(

#loj3090 [BJOI2019] 勘破神机

简单线性代数练习题 首先翻开具体数学生成函数一章,可以发现$F(n),G(n)$满足以下递推式 $$F(n)=F(n-1)+F(n-2),F(0)=1,F(1)=1$$ $$G(n)=4G(n-2)-G(n-4),G(2)=3,G(0)=1$$ 我们发现$G$只有偶数项有值(证明的话可以把网格黑白染色什么的来证明) 那么我们可以设$T(n)=G(2n)$ 那么$T$服从以下递推式 $$T(n)=4T(n-1)-T(n-2),T(1)=1,T(0)=1$$ 那么我们发现$F$和$G$是两个二阶常系

CF717A Festival Organization(第一类斯特林数,斐波那契数列)

题目大意:求 $\sum\limits_{n=l}^{r}\dbinom{f_n}{k}\bmod 10^9+7$.其中 $f_n$ 是长度为 $n$ 的 $01$ 序列中,没有连续两个或超过两个 $0$ 的个数. $1\le k\le 200,1\le l\le r\le 10^{18}$. 先考虑如何求 $f_n$. 令 $g[i][j]$ 表示长度为 $i$,结尾是 $j$ 的序列个数. $$g[i][0]=g[i-1][1]$$ $$g[i][1]=g[i-1][0]+g[i-1][1]

用for循环和递归调用写出1~N的斐波那契数列的和 和第N位的数

首先注意: 代码是从上往下,从左往右执行的!! 这是for循环写的 m=任意数.代表1~多少位的和 public class Fei_Bo_Na_Qi{    public static void main(String[] args){        int m = 30;  //这里代表1~30位的和        System.out.println( "斐波那契数列的第 "+m+" 位数为: "+m1(m) );//  在输出的时候调用函数    }   

javaScript实现回文数、水仙花数判断和输出斐波那契数列

    // 判断一个数是不是回文数                    // 方法一:先将数字转换成字符串,然后依次判断第一个和最后一个数字,第二个和倒数第二个数字...是否相等     function PalindromeNumber1(num){         var str = num.toString();         var flag = true;         var len = str.length;         for(var i = 0; i < (len 

给定斐波那契数列的项数求对应的数值的普通方法和优化处理

1 /** 2 给定斐波那契数列的项数求对应的数值 3 参考:剑指Offer 4 */ 5 #include <stdio.h> 6 7 int fib(int n); 8 long long fibonacci(unsigned int n); 9 int main(int argc, const char * argv[]) { 10 11 int n; 12 13 while (1) { 14 15 printf("请输入你想知道到的斐波那契数列的项数:\t"); 1

【c语言】求斐波那契数列的前40个数。特点,第1,2个数为1,从第三个数开始,该数是前面两个数之和

// 求斐波那契数列的前40个数.特点,第1,2个数为1,从第三个数开始,该数是前面两个数之和 #include <stdio.h> int main() { int a = 1; int b = 1; int c,i; printf("%d\t%d\t",a,b); for(i = 3; i <= 40; i++) { c = a + b; printf("%d\t",c); a = b; b = c; } printf("\n&quo

数楼梯——恶心的高精斐波那契数列

题目描述 楼梯有N阶,上楼可以一步上一阶,也可以一步上二阶. 编一个程序,计算共有多少种不同的走法. 输入输出格式 输入格式: 一个数字,楼梯数. 输出格式: 走的方式几种. 输入输出样例 输入样例#1: 4 输出样例#1: 5 说明 用递归会太慢,需用递推 (60% N<=50 ,100% N<=5000) 啊啊,数据太大了! 肿么办?! 当数据等于5000时的斐波那契数为 62763028004889570860352531083496840554785287027364574390258

奥数之斐波那契数列

今天群里出了一个算法题,感觉挺好玩,自己小学的时候数学也好,觉得能搞定,想了好久自己还是没找到解决方法,哎,看来智商还待二次发育.先说下题目: 50个台阶,一次可以走1步或2步,问有多少种走法? 看到这题我首先想到的是用for,类似2元钱可以有多少个1块.5毛.2毛.1毛组成,可是仔细想想还是不对,台阶这个是有顺序的,即使50步中只有一个是两步也有好几种情况,这样想着先从2步的算起,最多有26种,在这26种中还要排序,这样算下来脑袋都大了.还好,智慧在民间,群众的力量无限量,百度一下,网上有解决