Median Equals X

Median Equals X

范德蒙卷积,Hockey-Stick恒等式,计数问题

找到所有的a[i] = x, a[i]+a[j] = 2x
通过范德蒙卷积和Hockey-Stick恒等式进行快速统计即可

1. 问题转化

给定长度为 \(n\) 的序列 \(a_1,a_2,\dots,a_n\),以及目标值 \(x\)。

一个子序列由原序列的一组下标决定,即使选出的数值完全相同,只要下标集合不同,也算不同的子序列。

设选出的子序列长度为 \(m\),排序后为 \(v_1\le v_2\le \cdots \le v_m\)。

  • 若 \(m\) 为奇数,中位数为 \(v_{(m+1)/2}\);
  • 若 \(m\) 为偶数,中位数为 \(\frac{v_{m/2}+v_{m/2+1}}{2}\)。

要求中位数恰好等于 \(x\)。


2. 排序后按位置计数

将原序列排序,得到有序数组

\[a_0\le a_1\le \cdots \le a_{n-1} \]

排序不会改变“不同下标集合算不同子序列”这一事实,因为每个位置仍然对应原序列中的一个下标。

因此,我们只需要在排序后的数组中,按照位置选择子序列,并统计中位数为 \(x\) 的方案数。


3. 奇数长度子序列

若子序列长度为奇数,则中位数必须是某个具体元素,因此该元素必须等于 \(x\)。

枚举中位数所在位置 \(i\),要求

\[a_i=x \]

如果 \(i\) 是子序列的中位数,那么:

  • 在 \(i\) 左边选 \(k\) 个元素;
  • 在 \(i\) 右边选 \(k\) 个元素;
  • 总长度为 \(2k+1\),中位数正好是位置 \(i\) 的元素。

固定 \(i\) 时,方案数为

\[\sum_{k} \binom{i}{k}\binom{n-1-i}{k} \]

利用范德蒙卷积:

\[\sum_{k} \binom{i}{k}\binom{n-1-i}{k} = \sum_{k} \binom{i}{k}\binom{n-1-i}{n-1-i-k} = \binom{n-1}{i} \]

所以所有满足 \(a_i=x\) 的位置 \(i\),贡献为

\[\binom{n-1}{i} \]

代码中对应:

for(int i = 0; i < n; i ++) {
    if(a[i] == x) ans = (ans + C(n - 1, i)) % MOD;
}

4. 偶数长度子序列

若子序列长度为偶数,则中位数是两个中间元素的平均值。

设两个中间位置分别为 \(i<j\),它们必须满足

\[a_i+a_j=2x \]

此时:

  • \(i\) 是子序列排序后的第 \(k+1\) 个元素;
  • \(j\) 是子序列排序后的第 \(k+2\) 个元素;
  • 在 \(i\) 左边选 \(k\) 个元素;
  • 在 \(j\) 右边选 \(k\) 个元素;
  • \(i\) 和 \(j\) 之间不能选任何元素,否则中间位置会改变。

因此,固定 \(i,j\) 时,方案数为

\[\sum_{k} \binom{i}{k}\binom{n-1-j}{k} = \binom{i+n-1-j}{i} \]

这里同样使用了范德蒙卷积。

于是问题转化为:对于每个 \(i\),找到所有满足

\[a_j=2x-a_i,\qquad j>i \]

的位置 \(j\),并累加

\[\binom{i+n-1-j}{i} \]


5. 用 Hockey-Stick 恒等式快速求和

由于数组已经排序,所有满足 \(a_j=2x-a_i\) 的位置 \(j\) 一定形成一段连续区间。

设该区间为

\[j\in [l,r] \]

可以用 lower_bound 和 upper_bound 找到。

需要计算

\[\sum_{j=l}^{r} \binom{i+n-1-j}{i} \]

令

\[t=n-1-j \]

则求和变为

\[\sum_{t=n-1-r}^{n-1-l} \binom{i+t}{i} \]

利用 Hockey-Stick 恒等式:

\[\sum_{t=A}^{B} \binom{i+t}{i} = \binom{i+B+1}{i+1} - \binom{i+A}{i+1} \]

代入 \(A=n-1-r\),\(B=n-1-l\),得到

\[\sum_{j=l}^{r} \binom{i+n-1-j}{i} = \binom{i+n-l}{i+1} - \binom{i+n-r-1}{i+1} \]

代码中对应:

term1 = C(n - l + i, i + 1);
term2 = C(n - r + i - 1, i + 1);
contrib = (term1 - term2 + MOD) % MOD;

include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 998244353;
const ll N = 4e5 + 1;
vector<ll> fact(N, 0), invfact(N, 0);

ll modpow(ll a, ll b) {
    ll r = 1;
    while(b) {
        if(b & 1) r = r * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return r;
}
void init() {
    fact[0] = 1;
    for(int i = 1; i < N; i ++) fact[i] = fact[i-1] * i % MOD;
    invfact[N-1] = modpow(fact[N-1], MOD - 2);
    for(int i = N - 2; i >= 0; i --) invfact[i] = invfact[i + 1] * (i + 1) % MOD;
}
ll C(int m, int x) {
    if(x < 0 || x > m || m < 0) return 0;
    return fact[m] * invfact[x] % MOD * invfact[m - x] % MOD;
}

void solve() {
    int n; ll x; cin >> n >> x;
    vector<ll> a(n);
    for(int i = 0; i < n; i ++) cin >> a[i];
    sort(a.begin(), a.end());

    ll ans = 0;
    for(int i = 0; i < n; i ++) {
        if(a[i] == x) ans = (ans + C(n - 1, i)) % MOD;
    }
    for(int i = 0; i < n; i ++) {
        ll k = x * 2 - a[i];
        auto it = lower_bound(a.begin(), a.end(), k);
        auto it2 = upper_bound(a.begin(), a.end(), k);
        int l = it - a.begin(), r = it2 - a.begin();
        l = max(l, i + 1); r --;

        if(l <= r) {
            ll term1 = C(n - l + i, i + 1);
            ll term2 = C(n - r + i - 1, i + 1);
            ll contrib = (term1 - term2 + MOD) % MOD;
            ans = (ans + contrib) % MOD;
        }
    }
    cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    init();
    int _ = 1; cin >> _;
    while(_--) solve();
    return 0;
}
posted @ 2026-10-03 09:45  thenrify  阅读(14)  评论(0)    收藏  举报