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;
}