CF1999F.Expected Median 题解

一.前言

这是一道很有趣的数学题目,用到了逆元和组合数学,非常适合新手练手。

二.题目大意:

给出一个长度为 \(n\)\(01\) 数组。找出所有长度为 \(k\) 的子序列的中位数,求出其和。

妥妥的数学题~

三.分析

首先考虑对答案的贡献。

很明显,只有 \(1\) 作为中位数时才会做出贡献,于是抛弃 \(0\) 不管。

对于 \(1\),将序列排序后最少有 \(\frac{k+1}{2}\)\(1\)\(1\) 才是中位数,能对答案做出贡献。

举个栗子。当 k = 5 时,构造序列如下:

\(\left\lbrace0,0,1,1,1\right\rbrace\)

很明显,中位数在第 \(3\) 个位置,即 \(\frac{5+1}{2} = 3\),那么,此序列有多少个 \(1\) 呢?没错,\(5 - \frac{5+1}{2} + 1 = 3\) 个。非常好!用 \(k\) 替代数字 \(5\)\(k - \frac{k+1}{2} + 1 = \frac{k+1}{2}\)

所以,\(1\) 个数的范围应该是 \([\frac{k+1}{2},k]\)

这是奇数的栗子,当然,偶数也是如此,同学们可以自己尝试一下,我就不在赘述啦。其实是我懒啦。

这时,离成功仅有一步之遥。

枚举 \(1\) 的个数,考虑其为 \(i\) 时的情况。

很容易想到,从 \(1\) 的总个数里选出 \(i\) 个的方案数乘上从 \(0\) 的总个数里选出 \(k-i\) 个的方案数即可。

再对每个 \(i\) 分别计算再求和即可。

即:\(ans = \underset{i=x}{\overset{cnt}{\sum}} \binom{cnt}{i} \times \binom{n-cnt}{k-i}\)

\(cnt\)\(1\) 的总数,\(x\)\(1\) 最少的个数。

最后注意组合数的合适求法以及取模。

好,这道题目就讲完了,是不是非常简单呢?

最后贴一下代码。

四.代码

#include <bits/stdc++.h>
#define ll long long
#define pii pair <int, int>
using namespace std;
const int N = 2e5 + 10;
const ll mod = 1e9 + 7;
ll a[N]; ll ans, f[N];
inline ll ppow(ll a, ll b) {
	ll ret = 1;
	while (b) {
		if (b & 1) ret = ret * a % mod;
		a = a * a % mod; b >>= 1;
	}
	return ret;
}
inline ll C(ll n, ll k) {
	if (n < k) return 0;
	if (k < 0) return 0;
	ll ret = f[n] * ppow(f[k], mod - 2) % mod;
	ret = ret * ppow(f[n - k], mod - 2) % mod;
	//逆元,防止除法溢出 
	return ret;
}
inline void solve() {
	ll n, k; cin >> n >> k;
	ll x = (k + 1) / 2, cnt = 0;
	ans = 0;
	for (int i = 1; i <= n; ++i)
		cin >> a[i], cnt += a[i];
	for (int i = x; i <= cnt; ++i) {
		ans += C(cnt, i) * C(n - cnt, k - i) % mod;
		ans %= mod;
	}
	cout << (ans % mod) << "\n";
}
int main() {
	f[0] = 1;
	for (int i = 1; i <= N - 10; ++i)
		f[i] = f[i - 1] * i % mod;
	int T; cin >> T;
	while (T -- ) solve();
	return 0;
}

最后不要脸地求个赞。

QwQ

posted @ 2025-01-26 14:07  云岚天上飘  阅读(52)  评论(0编辑  收藏  举报

喜欢请打赏

扫描二维码打赏

了解更多