【組合數學】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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章