zoukankan      html  css  js  c++  java
  • luogu P5320 [BJOI2019]勘破神机

    upd:20.04.27

    orz EI

    考虑把组合数拆成下降幂除掉阶乘

    (sum_{i=0}^{n}inom{f_i}{k}=sum_{i=0}^{n}frac{{f_i}^{underline{k}}}{k!}=frac{1}{k!}sum_{i=0}^{n}{f_i}^{underline{k}})

    考虑算(sum_{i=0}^{n}{f_i}^{underline{k}}),带入通项公式(f_i=lambda_1alpha^i+lambda_2eta^i)

    (sum_{i=0}^{n}{f_i}^{underline{k}}=sum_{i=0}^{n}(lambda_1alpha^i+lambda_2eta^i)^{underline{k}})

    (=sum_{i=0}^{n}prod_{j=0}^{k-1}(lambda_1alpha^i+lambda_2eta^i-j))

    注意到本题中在(m=2)(alphaeta=-1),在(m=3)(alphaeta=1)

    所以先考虑(m=3),可得(eta=alpha^{-1}),代入得

    (=sum_{i=0}^{n}prod_{j=0}^{k-1}(lambda_1alpha^i+lambda_2alpha^{-i}-j))

    设后面柿子展开后((alpha^i)^j)前面系数为(c_j)

    (=sum_{i=0}^{n}sum_{j=-k}^{k}c_j(alpha^i)^j)

    (=sum_{j=-k}^{k}c_j(sum_{i=0}^{n}alpha^{ij}))

    所以用分治fft算出(c_j)后就可以等比数列求和做了

    还有(m=2)的情况,这时(alphaeta=-1,eta=-alpha^{-1}),代入后发现当(i)为奇数时(prod)里面(lambda_2)前面符号为负号,否则为正号,这时候分别算奇数偶数的答案即可.总复杂度(O(klog^2k+klog n))

    code


    以下是原内容

    传送门

    首先我们要知道要求什么.显然每次放方块要放一大段不能从中间分开的部分.设(m=2)方案为(f),(m=3)方案为(g),(m=2)可以放一个竖的,或者两个横的,所以(f_i=f_{i-1}+f_{i-2});(m=3),因为只有(i)为偶数有值,所以为了方便后面的(i)其实是原来的(2i),然后可以发现要么放三个横的,要么一横两竖,要么像下面这样放长度为偶数的块

    |--|   ------
    |--|   |----|
    ----   |----|
    

    注意到(原来的)长度为(2)有三种方案,其他的都有2种方案,所以(g_i=3g_{i-1}+sum_{j=2}^{i}2g_{i-j}),打表可以发现其实是(g_i=4g_{i-1}-g_{i-2}(g_1=3)).因为要从某种长度中选(k)个,所以要求((m=2)sum_{i=l}^r inom{f_i}{k}).显然只要考虑([1,r])([1,l-1])的值

    (k)较小,我们可以把组合数看成一个(k+1)次项的多项式(inom{n}{k}=frac{1}{k!}prod_{i=0}^{k}(n-i)),后面记第(i)项系数为(s_i),那么我们要求的是
    (sum_{i=1}^n sum_{j=0}^{k} s_j{f_i}^j)
    (sum_{j=0}^{k} s_jsum_{i=1}^n{f_i}^j)

    后面那个东西不大好求,考虑我们已经知道递推式了,可以利用特征方程解出通项公式,可以去这里学,所以(f)的通项公式是自己去网上找,然后稍微改一下,(g)的公式是(g_n=frac{3-sqrt{3}}{6}(2+sqrt{3})^n+frac{3+sqrt{3}}{6}(2-sqrt{3})^n),后面为了方便,统一记为(ab^n+cd^n)

    然后继续推式子
    (sum_{j=0}^{k} s_jsum_{i=1}^n(ab^i+cd^i)^j)

    二项式定理展开
    (sum_{j=0}^{k} s_jsum_{i=1}^nsum_{l=0}^{j}inom{j}{l} (ab^i)^l(cd^i)^{j-l})
    (sum_{j=0}^{k} s_jsum_{l=0}^{j}inom{j}{l}sum_{i=1}^n a^lb^ilc^{j-l}d^{i(j-l)})
    (sum_{j=0}^{k} s_jsum_{l=0}^{j}inom{j}{l}a^lc^{j-l}sum_{i=1}^n (b^ld^{j-l})^i)

    后面可以等比数列求和,然后直接做就行了

    注意(3)(5)在模(998244353)意义下没有二次剩余,所以把运算的数记为(a+bsqrt{c}),然后写个类型,定义一下运算就好了

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<vector>
    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<map>
    #include<set>
    #define LL long long
    #define db double
    
    using namespace std;
    const int N=1000+20,mod=998244353;
    LL rd()
    {
        LL x=0,w=1;char ch=0;
        while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*w;
    }
    int fpow(int a,int b){int an=1;while(b){if(b&1) an=1ll*an*a%mod;a=1ll*a*a%mod,b>>=1;} return an;}
    int inv(int a){return fpow(a,mod-2);}
    int kk,s[N],fac[N],iac[N],bs;
    int C(int n,int m){return m<0||n<m?0:1ll*fac[n]*iac[m]%mod*iac[n-m]%mod;}
    struct node
    {
        int a,b;
        node(){a=b=0;}
        node(int na,int nb){a=na,b=nb;}
        node operator + (const node &bb) const {return node((a+bb.a)%mod,(b+bb.b)%mod);}
        node operator - (const node &bb) const {return node((a-bb.a+mod)%mod,(b-bb.b+mod)%mod);}
        node operator * (const node &bb) const {return node((1ll*a*bb.a+1ll*b*bb.b%mod*bs)%mod,(1ll*a*bb.b+1ll*b*bb.a)%mod);}
        node operator * (const LL &bb) const {return node(1ll*a*bb%mod,1ll*b*bb%mod);}
        node invv(){int dt=inv((1ll*a*a-1ll*bs*b*b%mod+mod)%mod);return node(1ll*a*dt%mod,(mod-1ll*b*dt%mod)%mod);}
        node operator ^ (const LL &bb) const
        {
            node an,a=*this;
            an.a=1;
            LL bbb=bb;
            while(bbb)
            {
                if(bbb&1) an=an*a;
                a=a*a,bbb>>=1;
            }
            return an;
        }
    }a,b,c,d,yi;
    node dbsl(node aa,LL n){return aa.a==1&&aa.b==0?node(n%mod,0):(yi-(aa^n))*((yi-aa).invv());}
    int sov(LL n)
    {
        int an=0;
        for(int j=0;j<=kk;++j)
        {
            node sm;
            for(int l=0;l<=j;++l)
                sm=sm+(a^l)*(c^(j-l))*(dbsl((b^l)*(d^(j-l)),n+1)-yi)*C(j,l);
            an=(an+1ll*s[j]*sm.a%mod)%mod;
        }
        return 1ll*an*iac[kk]%mod;
    }
    
    int main()
    {
        yi.a=1;
        fac[0]=1;
        for(int i=1;i<=N-10;++i) fac[i]=1ll*fac[i-1]*i%mod;
        iac[N-10]=inv(fac[N-10]);
        for(int i=N-10;i;--i) iac[i-1]=1ll*iac[i]*i%mod;
        int T=rd(),op=rd();
        if(op==2)
        {
            bs=5;
            a=node(inv(2),inv(10)),b=node(inv(2),inv(2)),c=node(inv(2),mod-inv(10)),d=node(inv(2),mod-inv(2));
        }
        else
        {
            bs=3;
            a=node(inv(2),mod-inv(6)),b=node(2,1),c=node(inv(2),inv(6)),d=node(2,mod-1);
        }
        while(T--)
        {
            LL l=rd(),r=rd(),ln=(r-l+1)%mod;kk=rd();
            if(op==3) l=(l+1)/2+1,r=r/2+1;
            memset(s,0,sizeof(int)*(kk+3));
            s[0]=1;
            for(int i=0;i<kk;++i)
            {
                for(int j=kk;j;--j) s[j]=(1ll*s[j]*(mod-i)%mod+s[j-1])%mod;
                s[0]=1ll*s[0]*(mod-i)%mod;
            }
            printf("%lld
    ",1ll*(sov(r)-sov(l-1)+mod)%mod*inv(ln)%mod);
        }
        return 0;
    }
    
  • 相关阅读:
    验证码帮助类
    UDP聊天
    SoapHeader的使用
    单条目选择控件
    Redis命令总结
    PHP解决抢购、秒杀、抢楼、抽奖等阻塞式高并发库存防控超量的思路方法
    Python3经典100道练习题004
    Python3经典100道练习题001
    tkinter笔记01创建第一GUI
    Python3经典100道练习题006
  • 原文地址:https://www.cnblogs.com/smyjr/p/10770506.html
Copyright © 2011-2022 走看看