一.前言
这是一道很有趣的数学题目,用到了逆元和组合数学,非常适合新手练手。
二.题目大意:
给出一个长度为 \(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
标签:cnt,frac,CF1999F,题解,ll,ret,Expected,return,mod From: https://www.cnblogs.com/2026zhaoyl/p/18351526