zoukankan      html  css  js  c++  java
  • 牛客挑战赛39 E 牛牛与序列题解

    一.闲谈

    这次比赛真惨,B题我的数据分块被卡了,只有75分qwq,C题打分块和线段树都被卡了,我好难啊。。。

    然后,看了下D题,emmm算了下一题,一看E题,哇数论题,于是操起草稿纸开干了。。。

    二.题解

    题目叫你求长度为n的序列,且数列元素为[1,k]的整数,且同时存在严格上升和严格下降的两个位置的序列的个数。

    我一开始打算直接正着做,发现很不好搞,于是,正难则反嘛。。。我们就反着做

    Ans=所有方案数-不同时存在严格上升和严格下降的序列数

    注意到,所有方案数就是(k^n),这个可以直接跑一遍卡速米

    然后,我们就是要求后面那一节了。

    我们发现,不同时存在严格上升和严格下降的序列一定满足:

    对于(forall iin[2,n]a_i<=a_{i-1}或者forall iin[2,n]a_i>=a_{i-1})

    那么,我们只需要统计这两个就好了。

    注意到,这两种方案中,其中一种的倒序一定满足另一种,所以我们只需要求出一种的方案数x,另一种的的方案数便一定也是x了,不过,当所有(a_i)取相同值的时候,会同时满足两个序列,所以两种的方案数之和应再减k

    现在就是如何求其中一种的方案了

    一开始,我不想算了,直接打了一组表:

    n        k        ans
    2        1        1        C(2,2)
    2        2        3        C(3,2)
    2        3        6        C(4,2)
    2        4        10       C(5,2)
    3        1        1        C(3,3)
    3        2        4        C(4,3)
    3        3        10       C(5,3)
    3        4        20       C(6,3)
    4        1        1        C(4,4)
    4        2        5        C(5,4)
    4        3        15       C(6,4)
    4        4        35       C(7,4)
    

    经过观测发现,这不就是k维前缀和有规律的组合数嘛?

    找下规律就会发现,方案数就等于C(n+k-1,n)=C(n+k-1,k-1)

    我们来理解下公式的意义:

    现在我们求的是有n个数满足这n个数都属于([1,k])且严格不下降(或者严格不上升)的方案数

    其实公式的意义就相当于,我们先挖n+k-1个空,再从这些空里面选出k-1个空作为“隔板”

    开头到第一个隔板之间的空填1,第一个隔板到第二个隔板填2。。。第k-1个隔板和末尾之间填k

    这样,我们就成功的获得了一个长度为n(剩余n个空填数),严格不下降(每个隔板段中填的数依次递增)的方案数。(由于两个隔板可能相邻,所以,也有些数其实没有空填,所以所有方案都包含在内)

    现在我们来解题

    首先我们转换成公式形式:

    (Ans=frac{(n+k-1)!}{n!*(k-1)!})

    约分一下:

    (Ans=frac{prod_{i=n+1}^{n+k-1}i}{(k-1)!})

    读题的范围:(k<=10^6,sum k<=3*10^7)

    所以,我们直接先O(n)预处理出(1-10^6的阶乘的逆元)再在每次询问时,暴力计算答案的分子,最后直接处理答案即可~

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=998244353,N=1e6+1;
    int n,k;
    int ni[N],sp[N];
    inline int ksm(int x,int y){
        int ans=1;
        while(y){
            if(y&1){
                ans=(1LL*ans*x)%mod;
            }
            x=(1LL*x*x)%mod;
            y>>=1;
        }
        return ans%mod;
    }
    int main(){
        ni[1]=1,sp[0]=sp[1]=1;
        for(int i=2;i<N;++i){
            ni[i]=(1LL*ni[mod%i]*(mod-mod/i))%mod;
            sp[i]=(1LL*sp[i-1]*ni[i])%mod;
        }
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&k);
            if(k==1){//特判1
                puts("0");
                continue;
            }
            int tot=ksm(k,n);
            tot=(tot+k)%mod;
            int kil;
            if(k==2){
                kil=(n+1)%mod;
            }else{
                long long X=k+(n-1),Y=n;
                kil=1;
                for(int i=Y+1;i<=X;++i){
                    kil=(1LL*kil*i)%mod;
                }
                kil=(1LL*kil*sp[k-1])%mod;
            }
            tot-=(kil*2)%mod;
            tot=(tot%mod+mod)%mod;
            printf("%d
    ",tot);
        }
        return 0;
    }
    
  • 相关阅读:
    [转]HTML5 classList API
    高质量CSS编写规范
    CSS Hack
    谈谈JSON数据格式
    Eclipse启动时报需要安装"Java SE 6 Runtime"致无法启动解决方案
    站长问题纠结
    做站长的经验
    网站如何提高PR值
    Progit Update Check Page
    WPF随手小记之二 ——改变DataGrid样式
  • 原文地址:https://www.cnblogs.com/ThinkofBlank/p/12724541.html
Copyright © 2011-2022 走看看