【组合数学】CF1312D Count the Arrays

Educational Codeforces Round 83 (Rated for Div. 2) D.Count the Arrays
题目链接

题目描述

有一个长 n 的数组,数字范围在 1~m,这个数组中存在一对相同的数字,同时对于这个数组存在一个最大值使得这个数组严格递增并递减(呈现一个山峰形状),求有几个数组,答案对998244353取模

思路

取 n-1个数,做单调递增放左边,则有C[n-1][m] 种取法,然后再从左边的 n -2 个数中找一个数使得相等,最后枚举这个最大值的位置,也就是选择左边的几个数移到右边去,有 n - 3 个数,那么有
C[0][n-3] + C[1][n-3] + C[2][n-3] + … + C[n-4][n-3] + C[n-3][n-3] = 2^(n-3)
所有答案就是 C[n-1][m] * (n-2)*2^(n-3)

补一个组合数的知识点

乘法逆元+快速幂+阶乘的方法来给出取模组合数的模板
(a / b) mod m = ( a * k ) mod m; (k为b的逆元,m为模数)
因为将除法的模运算转化成乘法的模运算需要逆元,所以给出用费马小定理求逆元的原理
根据费马小定理:ap−1 ≡ 1 ( mod p)
两边同除a可推出:ap-2 ≡ 1/a ( mod p)
所以可得出 1/a 就是 a 的逆元
这个逆元 inv(a) 可用快速幂ap-2获得

模板

求C[n][m]

const ll p=100000007;		//模数,瞎写一个
ll kpow(ll a, ll n, ll p) {
	ll tmp=a;
	ll res=1;
	while(n) {
		if (n&1) res=res*tmp%p;
		n>>=1;
		tmp=tmp*tmp%p;
	}
	return res;
}
ll inv(ll x) {
	return kpow(x, mod-2, mod);
}
void solve() {
	ll num=1;
	for(int i=1; i<=m; i++) num=num*i%p;			//m!
	for(int i=1; i<=n; i++) num=num*inv(i)%p;		//除以n!
	for(int i=1; i<=m-n; i++) num=num*inv(i)%p;		//除以(m-n)! 
}

代码片

//CF-1312D
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const ll mod=998244353;

ll kpow(ll a, ll n, ll p) {		//快速幂
	ll tmp=a;
	ll res=1;
	while(n) {
		if (n&1) res=res*tmp%p;
		n>>=1;
		tmp=tmp*tmp%p;
	}
	return res;
}

ll inv(ll x) {		//逆元
	return kpow(x, mod-2, mod);
}

void solve() {
	ll n,m;
	scanf("%lld %lld",&n,&m);
	if ( n==2 ) {
		printf("0\n");
		return;
	}
	ll ans=n-2;
	//求C[n-1][m]
	for(int i=1; i<=m; i++) ans=ans*i%mod;
	for(int i=1; i<=n-1; i++) ans=ans*inv(i)%mod;
	for(int i=1; i<=m-n+1; i++) ans=ans*inv(i)%mod;
	//
	ans=ans*kpow(2,n-3,mod)%mod;
	printf("%lld\n",ans);
}

int main() {
//	freopen("in.txt","r",stdin);
	solve();
	return 0;
}
发布了13 篇原创文章 · 获赞 6 · 访问量 573

猜你喜欢

转载自blog.csdn.net/zx1020354953/article/details/104786655