zoukankan      html  css  js  c++  java
  • [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
    ",(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
    ",(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;
    }
    
  • 相关阅读:
    windows7 端口查看以及杀死进程释放端口
    字符设备驱动模块与测试代码编写。
    c++项目范例
    较复杂makefile跟lds脚本程序的编写
    S5PV210时钟,看门狗定时器
    S5PV210中断处理
    arm 异常处理结构
    arm指令系统
    arm体系结构
    s5pv210 的启动
  • 原文地址:https://www.cnblogs.com/Creed-qwq/p/10766003.html
Copyright © 2011-2022 走看看