牛客挑战赛39 E 牛牛与序列题解

一.闲谈

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

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

二.题解

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

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

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

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

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

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

对于\(\forall i\in[2,n]a_i<=a_{i-1}或者\forall i\in[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\n",tot);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ThinkofBlank/p/12724541.html
今日推荐