首页 > 试题广场 >

中位数之和

[编程题]中位数之和
  • 热度指数:1951 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\hspace{15pt}给定一个长度为 n二进制数组 a(每个元素为 01)。记 k 为奇数。对于数组 a 的所有长度恰为 k子序列,求它们中位数之和,并对 P=1\,000\,000\,007 取模。

【名词解释】
\hspace{15pt}子序列如果 b 可以从 a 中删除几个元素(可能是零,也可能是全部元素),那么数组 b 就是数组 a 的子序列。子序列不必是连续的
\hspace{15pt}中位数长度为奇数 k 的数组的中位数是排序后的第 \frac{k+1}{2} 个元素。

输入描述:
\hspace{15pt}第一行输入整数 t\left(1\leqq t\leqq 10^4\right) 表示测试用例个数。 
\hspace{15pt}对于每个测试用例:
\hspace{23pt}\bullet\, 第一行输入两个整数 n,k,满足 1\leqq k\leqq n\leqq 2\times10^5k 为奇数;
\hspace{23pt}\bullet\, 第二行输入 n 个整数 a_i\in\{0,1\}
\hspace{15pt}保证所有测试用例的 n 之和不超过 2\times10^5


输出描述:
\hspace{15pt}对每个测试用例输出一行一个整数,表示所有长度为 k 的子序列中位数之和模 P 的结果。
示例1

输入

1
5 1
1 1 1 1 1

输出

5

说明

所有长度为 1 的子序列的中位数为 1 ,因此答案为 5 。

逛了一圈发现没有我的做法,就写一个吧,详情可以看我的b站视频****************************************
哎呀呀,这种一眼就能看出是组合数学的题目,居然还要本天才翔子出马?你们这些杂鱼的大脑难道和二进制数组里的 0 一样,完全没有存在的价值吗?呵呵~

既然你们求知欲这么旺盛(其实是太笨了吧?),那我就勉为其难地给你们这群逊毙了的家伙写篇题解。好好盯着看,这可是天才的恩赐!


📊 翔子的天才课堂:二进制中位数的秘密

🧐 核心逻辑:中位数才不是随便当的!

听好了,笨蛋杂鱼们!这道题的数组只有 0 和 1。

  1. 中位数的本质:对于长度为 的子序列,中位数是 1 的充分必要条件是——这个子序列里至少有 个 1
  2. 固定中位数的策略:我们把原数组从小到大排个序(虽然里面全是 0 和 1,但排序后 0 都在左边, 1 都在右边)。
  3. 计数大法
  • 假设我们要让第 个位置的数 成为中位数。
  • 我们需要在它的左边( 个位置)选出 个数。
  • 我们需要在它的右边( 个位置)选出 个数。
  • 这样总共就选了 个数,且 刚好在中间。
  • 既然只有 的时候才会对总和有贡献,那我们只需要算 时的组合数就行了!

这种 的预处理加 的遍历,连杂鱼都能跑通,要是你还写错,建议直接退出代码界呢~


💻 毫无难度的 C++ 代码

这种基础的逆元和阶乘预处理,要是杂鱼不会写,翔子真的会笑出声的哦!

#include <bits/stdc++.h>
#define il inline
#define double long double
using namespace std;
using ll = long long;
using ull = unsigned long long;
using int128 = __int128_t;

const ll N = 2e5 + 5, mod = 1e9+7, inf = 2e18;
const double eps = 1e-9;
double PI = 3.1415926;

// 杂鱼才需要写注释,但我还是帮你们写了,感谢我吧!
ll quick_mi(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1){
            ans=ans*a%mod;
        }
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}

// 逆元都不会算的杂鱼可以去重修小学数学了
ll inv(ll n){
    return quick_mi(n,mod-2);
}

vector<ll>fact(N,1),inv_fact(N);

// 组合数公式 C(n, m),杂鱼记住了吗?
ll C(ll n,ll m){
    if(m<0||m>n)return 0; // 选不出来就是0,和杂鱼的智商一样
    return fact[n]*inv_fact[m]%mod*inv_fact[n-m]%mod;
}

il void solve() {
    int n,k;
    cin>>n>>k;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    // 排序后 1 都在右边,方便我们固定中位数
    sort(a.begin()+1,a.end());
    ll ans=0;
    ll need=k/2; 
    for(int i=1;i<=n;i++){
        // 只有 a[i]=1 才有贡献,杂鱼要是这里写错就逊爆了
        (ans+=C(i-1,need)*C(n-i,need)*a[i]%mod)%=mod;
    }
    cout<<ans<<'\n';
}

int main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    int t = 1;

    // 预处理阶乘,为了拯救杂鱼的时间复杂度
    for(int i=1;i<N;i++){
        fact[i]=fact[i-1]*i%mod;
    }
    inv_fact[N-1]=inv(fact[N-1]);
    for(int i=N-2;i>=0;i--){
        inv_fact[i]=inv_fact[i+1]*(i+1)%mod;
    }

    cin >> t;

    while (t--){
        solve();
    }
    return 0;
}

🐍 慢吞吞的 Python 代码

虽然 Python 慢得像杂鱼爬行,但逻辑还是一样简单的呢。

import sys, math, queue, itertools, heapq
from collections import deque
from array import array
from bisect import bisect_right

# 这个递归深度,杂鱼的大脑能承受吗?
sys.setrecursionlimit(1145141919)
input = sys.stdin.readline

def read():
    line = sys.stdin.readline()
    if not line:
        return []
    return list(map(int, line.split()))

mod = 10**9 + 7
N = 2 * (10**5) + 5

def quick_mi(a, b):
    ans = 1
    while b:
        if b & 1:
            ans = ans * a % mod
        b >>= 1
        a = a * a % mod
    return ans

def inv(n):
    return quick_mi(n, mod - 2)

fact = [1] * N
inv_fact = [0] * N

def C(n, m):
    if m < 0 or m > n:
        return 0
    return fact[n] * inv_fact[m] * inv_fact[n - m] % mod

def solve():
    n, k = read()
    need = k // 2
    a = [0] + read()
    # 居然要手动排序,Python 真是麻烦呢,呵呵~
    a.sort(key=lambda x: x)
    ans = 0
    for i in range(1, n + 1):
        # 左右两边各选 need 个,中间坐个 1
        ans += a[i] * C(i - 1, need) * C(n - i, need)
        ans %= mod
    print(ans)

def main():
    # 预处理是给杂鱼最后的仁慈
    for i in range(1, N):
        fact[i] = fact[i - 1] * i % mod
    inv_fact[N - 1] = inv(fact[N - 1])
    for i in range(N - 2, -1, -1):
        inv_fact[i] = inv_fact[i + 1] * (i + 1) % mod

    line = sys.stdin.readline()
    if not line: return
    t = int(line)
    for _ in range(t):
        solve()

if __name__ == "__main__":
    main()

🙄 翔子的最后嘲讽

看明白了吗?这道题的精髓就是:只要你不是个连组合数都不会算的杂鱼,你就能过!

固定中位数位置,左右两边各选一半,这么简单的逻辑要是还要我讲第二遍,我就要把你的准考证撕掉喽~ 呵呵。赶紧去提交吧,别在这里碍眼了!

编辑于 2026-02-13 12:40:45 回复(2)
这道题是一道简单的数学题喵~

因为数组里只有0和1,所以当1占k的一多半的时候中位数必定是1喵!
中位数是0的情况不考虑喵~因为对结果没有贡献。
这道题好贴心的让k是奇数了喵~所以1最少数量是k/2+1。
假如1的数量是 i 个,那么0的数量是 k-i 个。
那么答案就会加上 喵!
把 i 的数量全部遍历一遍就可以了
没想到最难的居然是逆元的函数应该怎么写喵!
#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const ll mod=1e9+7;

vector<ll> jie(2e5+1);

ll ni(ll n)//求逆元
{
    ll ans=1;
    ll m=mod-2;
    while(m)
    {
        if(m&1) ans=ans*n%mod;
        n=n*n%mod;
        m>>=1;
    }
    return ans;
}

ll C(ll n,ll m)//求C(n,m)
{
    return jie[n]*ni(jie[m]*jie[n-m]%mod)%mod;
}

void solve()
{
    ll n,k;cin >> n >> k;
    ll n0=0,n1=0;
    for(ll i=0;i<n;i++)
    {
        ll num;cin >> num;
        if(num) n1++;
        else n0++;
    }//得到0和1的数量
    ll need=k/2+1;//最少需要的1的数量
    ll ans=0;//结果
    for(ll i=need;i<=min(n1,k);i++)//1的数量从小到最大
    {
        if(k-i>n0) continue;
        ans=(ans+C(n1,i)*C(n0,k-i))%mod;
    }
    cout << ans <<'\n';
}

int main ()
{
    cin.tie(0)->sync_with_stdio(0);
    jie[0]=1;
    for(ll i=1;i<=2e5;i++)//预处理阶乘
    {   
        jie[i]=jie[i-1]*i%mod;
    }
    int t;cin >> t;
    while(t--) solve();
}
/*
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⡀⣀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡀⢀⣴⣾⣷⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⠿⠛⠛⠉⠉⠉⠉⠉⠉⠛⠻⠿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⡿⠿⠛⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣿⣷⣄⡀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣀⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⣰⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣶⣄⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣻⣿⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢹⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⠁⠈⢢⡀⠀⠀⠀⢸⡇⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⡟⠒⢦⡀⠀⠀⠀
⠀⠀⣠⣤⣤⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡇⠀⠀⠀⠉⢢⣄⠀⠀⢿⠊⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⡄⠀⢷⠀⠀⠀
⠀⢰⠇⠀⣰⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⡌⣹⠗⠦⣬⣇⠀⠉⢢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡀⢸⡄⠀⠀
⠀⡟⠀⣼⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣆⢹⡀⠀⠀⠀⠉⠁⠀⠀⢀⣀⡁⠀⠀⠉⠳⢴⡆⠀⠀⠀⠀⠀⠀⢹⣧⠈⡇⠀⠀
⠀⡇⠀⠀⢻⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣾⠻⠉⠛⠂⠀⠀⠀⠀⠀⠀⠻⠿⣿⣿⣿⣶⣦⡀⠛⣇⠀⠀⠀⠀⠀⣈⣿⠀⡇⠀⠀
⢸⡇⠀⠀⢠⣿⣷⣦⣀⡸⣷⣦⣶⡂⠉⠉⠉⢁⣤⣶⡶⠀⠀⠀⣀⣀⡴⠀⠀⠀⠀⠀⠀⠈⠉⠉⠁⠀⡟⢀⣴⣟⣰⣾⣿⣏⣠⠇⠀⠀
⠈⡇⠀⠀⢸⣿⠁⠉⣿⠛⠛⠃⡇⠀⠀⢠⣶⣿⡿⠛⠁⠀⠀⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⢿⠟⠿⢿⡏⠀⠘⣿⡀⠀⠀⠀
⠀⢷⣀⣀⣿⠇⠀⠀⢿⡇⠀⢀⢱⡀⠀⠛⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠀⠀⢸⠇⠀⠀⢹⣿⣄⠀⠀
⠀⠀⣉⣿⡏⠀⠀⠀⠀⠀⠀⢸⣇⣳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⣿⠃⠀⠀⠀⠀⠀⠀⣿⠈⢧⠀
⠀⠘⣿⣿⠁⠀⠀⠀⠀⠀⠀⠘⣿⡛⣶⠀⠀⣠⠔⠒⠛⠒⠦⡀⠀⠀⠀⠀⣠⡤⠶⠤⢤⣀⠀⠀⠀⢀⣏⡄⠀⠀⠀⠀⠀⡀⣿⡆⠈⣧
⣠⡾⠛⣿⣿⣧⠀⠀⠀⠀⢸⣿⠾⢿⡿⠀⣰⠃⠀⠀⠀⠀⠀⢹⡄⠀⠀⡼⠁⠀⠀⠀⠀⠈⠙⣦⠀⢸⣿⡇⣾⣣⡀⠀⢰⣿⣿⣿⣤⠾
⡟⠀⠀⠻⣿⡟⢷⡄⣤⡀⠈⣿⡀⣸⠇⠀⠏⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⡇⢀⡀⠀⠀⠀⠀⢀⡟⠀⠀⠋⣿⣿⣿⡇⣠⣿⠿⠛⢷⡀⠀
⠀⠀⠀⠀⣿⣇⣨⣿⣿⣿⣦⣽⣷⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠃⠀⠙⠢⠤⠤⠴⢾⠀⠀⠀⠀⢸⣷⣿⣿⠟⠁⠀⠀⠈⣧⠀
⠀⠀⠀⠀⠈⠉⠉⠁⠈⠉⠈⢉⣿⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⣿⠀
*/


发表于 2026-02-13 01:39:15 回复(1)