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(n,k)=(An-Bn)k/(√5)k,然后可以用二项式定理展开,但模数太差是998244353,所以要扩域。其实这道题是一道原题,原题链接:CF717A

m=3,因为也是二阶递推式,所以解法是一样的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=600,mod=998244353;
int c[N][N],s[N][N];
int qpow(int a,ll b)
{
    int ret=1;
    while(b)
    {
        if(b&1)ret=1ll*ret*a%mod;
        a=1ll*a*a%mod,b>>=1;
    }
    return ret;
}
struct num{
    int a,b,c;
    num operator+(num x){return (num){(a+x.a)%mod,(b+x.b)%mod,c};}
    num operator-(num x){return (num){(a-x.a+mod)%mod,(b-x.b+mod)%mod,c};}
    num operator*(num x)
    {return (num){(1ll*a*x.a+1ll*c*b%mod*x.b)%mod,(1ll*a*x.b+1ll*b*x.a)%mod,c};}
    num inv()
    {
        int f=qpow((1ll*a*a-1ll*b*b%mod*c%mod+mod)%mod,mod-2);
        return (num){1ll*a*f%mod,1ll*(mod-b)*f%mod,c};
    }
    bool operator==(num x){return a==x.a&&b==x.b&&c==x.c;}
    num operator/(num x){return (*this)*x.inv();}
};
num qpow(num a,ll b)
{
    num ret=(num){1,0,a.c};
    while(b)
    {
        if(b&1)ret=ret*a;
        a=a*a,b>>=1;
    }
    return ret;
}
int F(ll n,int k)
{
    if(!k)return n%mod;
    n++;
    num ans=(num){0,0,5},A=(num){1,1,5}/(num){2,0,5},B=(num){1,mod-1,5}/(num){2,0,5};
    num x=(num){1,0,5},y=(num){1,0,5};
    for(int j=1;j<=k;j++)y=y*B;
    for(int j=0;j<=k;j++)
    {
        int ret=c[k][j];
        if(k-j&1)ret=(mod-ret)%mod;
        num t=x*y;
        if(t==(num){1,0,5})ans=ans+(num){n%mod*ret%mod,0,5};
        else ans=ans+(num){ret,0,5}*(qpow(t,n+1)-t)/(t-(num){1,0,5});
        x=x*A,y=y/B;
    }
    for(int j=1;j<=k;j++)ans=ans/(num){0,1,5};
    return (ans.a+mod-1)%mod;
}
int calf(ll n,int k)
{
    if(!n)return 0;
    int ret=0,sum;
    for(int j=0;j<=k;j++)
    sum=1ll*F(n,j)*s[k][j]%mod,ret=(k-j&1)?(ret-sum+mod)%mod:(ret+sum)%mod;
    for(int i=1;i<=k;i++)ret=1ll*ret*qpow(i,mod-2)%mod;
    return ret;
}
int G(ll n,int k)
{
    if(!k)return n%mod;
    num ans=(num){0,0,3},x1=(num){2,1,3},x2=(num){2,mod-1,3};
    num A=(num){3,1,3},B=(num){3,mod-1,3},x=(num){1,0,3},y=x,a=x,b=x;
    for(int j=1;j<=k;j++)y=y*x2,b=b*B;
    for(int j=0;j<=k;j++)
    {
        int ret=c[k][j];
        num t=x*y;
        if(t==(num){1,0,3})ans=ans+a*b*(num){n%mod*ret%mod,0,3};
        else ans=ans+(num){ret,0,3}*a*b*(qpow(t,n+1)-t)/(t-(num){1,0,3});
        x=x*x1,y=y/x2,a=a*A;b=b/B;
    }
    for(int j=1;j<=k;j++)ans=ans/(num){6,0,3};
    return ans.a;
}
int calg(ll n,int k)
{
    if(!n)return 0;
    n/=2;
    int ret=0,sum;
    for(int j=0;j<=k;j++)
    sum=1ll*G(n,j)*s[k][j]%mod,ret=(k-j&1)?(ret-sum+mod)%mod:(ret+sum)%mod;
    for(int i=1;i<=k;i++)ret=1ll*ret*qpow(i,mod-2)%mod;
    return ret;
}
int main()
{
    for(int i=0;i<=590;i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    }
    s[0][0]=1;
    for(int i=1;i<=590;i++)
    for(int j=1;j<=i;j++)
    s[i][j]=(s[i-1][j-1]+1ll*(i-1)*s[i-1][j])%mod;
    int T,m;scanf("%d%d",&T,&m);
    while(T--)
    {
        ll l,r;int k;cin>>l>>r>>k;
        int ans=m==2?(calf(r,k)-calf(l-1,k)+mod)%mod:(calg(r,k)-calg(l-1,k)+mod)%mod;
        ans=1ll*ans*qpow((r-l+1)%mod,mod-2)%mod;
        printf("%d\n",ans);
    }
}

原文地址:https://www.cnblogs.com/hfctf0210/p/10746324.html

时间: 2024-08-29 21:15:18

BJOI2019勘破神机(斯特林数+二项式定理+数学)的相关文章

[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^{

#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$是两个二阶常系

Noi2016十连测第二场-黑暗 (二项式定理/斯特林数+CDQ+NTT)

Noi2016十连测第二场-黑暗 (二项式定理/斯特林数+CDQ+NTT) 题意: n 个点的无向图,每条边都可能存在,一个图的权值是连通块个数的 m 次方,求所有可能的图的权值和. 考虑\(dp[i][j]\)表示\(j\)个点,权值为\(i\)次方 我们首先要预处理出\(n\)个点无向联通图的数量\(g[i]\),模板题:BZOJ-3456 题解 对于\(dp[i][j]\),枚举\(1\)号点所在的连通块大小为\(x\),那么可以得到的是\(dp[i][j]=\sum dp[k][j-x]

【2019雅礼集训】【第一类斯特林数】【NTT&amp;多项式】permutation

目录 题意 输入格式 输出格式 思路: 代码 题意 找有多少个长度为n的排列,使得从左往右数,有a个元素比之前的所有数字都大,从右往左数,有b个元素比之后的所有数字都大. n<=2*10^5,a,b<=n 输入格式 输入三个整数n,a,b. 输出格式 输出一个整数,表示答案. 思路: 这道题是真的神啊... 首先,根据官方题解的思路,首先有一个n^2的DP: 定义dp[i][j]表示一个长度为i的排列,从前往后数一共有j个数字大于所有排在它前面的数字. 首先有转移式: \[dp[i][j]=d

HDU3625(SummerTrainingDay05-N 第一类斯特林数)

Examining the Rooms Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1661    Accepted Submission(s): 1015 Problem Description A murder happened in the hotel. As the best detective in the town, yo

Gym 101147G 第二类斯特林数

大致题意: n个孩子,k场比赛,每个孩子至少参加一场比赛,且每场比赛只能由一个孩子参加.问有多少种分配方式. 分析: k>n,就无法分配了. k<=n.把n分成k堆的方案数乘以n的阶乘.N分成k堆得方案数即第二类斯特林数 http://blog.csdn.net/acdreamers/article/details/8521134 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll

hdu 4041 2011北京赛区网络赛F 组合数+斯特林数 ***

插板法基础知识 斯特林数见百科 1 #include<iostream> 2 #include<cmath> 3 #include<cstdio> 4 #include<cstring> 5 #define LL long long 6 #define eps 1e-7 7 #define MOD 1000000007 8 using namespace std; 9 int c[2001][2001]={1},stir2[1005][1005]={1};

hdu 3625 第一类斯特林数

题目链接:click here Examining the Rooms Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1146    Accepted Submission(s): 689 Problem Description A murder happened in the hotel. As the best detective

Light OJ 1236 Race 第二类斯特林数

第二类斯特林数 n 匹马 分成1 2 3... n组 每一组就是相同排名 没有先后 然后组与组之间是有顺序的 在乘以组数的阶乘 #include <cstdio> #include <cstring> using namespace std; int dp[1010][1010]; int a[1010]; int main() { a[0] = 1; dp[0][0] = 1; for(int i = 1; i <= 1000; i++) { dp[i][0] = 0; d