Codeforces - 1312D - Count the Arrays(组合数学、逆元)

题目链接
题目大意:让你从\(m\)个数里面挑出来若干个数构成一个\(n\)个数的序列,且这个序列有且仅有一对相等的数。并且,这个序列在一个数字之前严格单调递增,在同一个数字之后严格单调递减。问你最多能组成多少个这样的序列。
  首先,题目已经告诉我们了,我们要求的序列是一个单峰序列,所以如果\(n=2\)的话是不能构成这样的序列的(坑点)。
  然后我们开始分析,要构成一个含有\(n\)个数并且有且只有一对数字重复的序列需要\(n-1\)个数,所以选法一共有\(C{{n-1}\atop{m}}\)种,对于这\(n-1\)个数,因为是单峰的,所以最大的数的位置一定是固定不变的,又要左右两边分别严格单调,所以位置也不能变。所以,能重复出现的数字一共有\(n-2\)种。最后我们还要考虑左右数字移动的问题。因为相等的数字不能出现在同一边,而且最大的数不能动,所以就只有\(n-3\)个数可以动,因为这\(n-3\)个数字都不相同,显然,它们中的任意一个数都能移动到另一边。那么对于\(n-3\)个数字中的每一个数都有在某一边和不在某一边两种情况,也就是\(2^{n-3}\)种情况。
  综上所述,我们的答案就是\(C{{n-1}\atop{m}}\times (n-2)\times 2^{n-3}\),这里组合数比较大,所以我们取模的时候需要用到逆元的知识,然后用快速幂求解即可。

ll qpow(ll x, int y) {
    ll res = x, ans = 1;
    while(y) {
        if (y&1) ans = ans*res%MOD;
        res = res*res%MOD;
        y >>= 1;
    }
    return ans;
}
int main(void) {
    ll n, m;
    while(~scanf("%lld%lld", &n, &m)) {
        if (n==2) {
            printf("0\n");
            continue;
        }
        ll fac1 = 1, fac2 = 1;
        for (int i = m; i>m-n+1; --i)
            fac1 = fac1*i%MOD;
        for (int i = n-1; i>1; --i)
            fac2 = fac2*i%MOD;
        printf("%lld\n", fac1*qpow(fac2, MOD-2)%MOD*(n-2)%MOD*qpow(2, n-3)%MOD);
    }
    return 0;
}
posted @ 2020-03-26 11:10  shuitiangong  阅读(142)  评论(0编辑  收藏  举报