CF1005E1题解报告

一道前缀和的好题。

正文

我们可以考虑在什么情况下中位数是 \(m\)

不难发现,子段长度为奇数时,\(< m\) 的个数与 \(> m\) 的个数相等。
长度为偶数时,\(< m\) 的个数比 \(> m\) 的个数少 \(1\)

对于求区间内有多少个比 \(m\) 大(小)的个数,可以考虑线段树。啊呸!开个玩笑,肯定考虑前缀和。

我们可以用 \(p1_i\) 表示前 \(i\) 个数中有多少个比 \(m\) 小。用 \(p2_i\) 表示多少个比 \(m\) 大。以上式子就可以表示成这样( \(l, r\) 为子段左右端点,左开右闭):

\[\begin{cases} p1_r - p1_{l} = p2_r - p2_l & l - r \equiv 1 \pmod{2} \\ p1_r - p1_l + 1 = p2_r - p2_l & \text{otherwise.} \end{cases} \]

稍微移项可得:

\[\begin{cases} p1_r - p2_r = p1_l - p2_l & l - r \equiv 1 \pmod{2} \\ p1_r - p2_r + 1 = p1_l - p2_l & \text{otherwise.} \end{cases} \]

注意到左右两边相互独立,可以统计 \(p1_l - p2_l\) 的个数,用 map 存储即可,最后枚举 \(r\) 即可。

这里还有个细节,令 \(m\) 的位置为 \(p\),有 \(l \le p \le r\)。所以 map 只用存 \(l \le p\) 时的 \(p1_l - p2_l\)\(r\)\(p\) 开始枚举即可。

code

#include<bits/stdc++.h>

using namespace std;

const int N = 2e5 + 1;

int n, m, a[N], p1[N], p2[N], p;
map<int, int> mp;
long long ans;

int main(){
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  mp[0] = 1;
  for(int i = 1; i <= n; i++){
    cin >> a[i];
    p1[i] = p1[i - 1] + (a[i] < m);
    p2[i] = p2[i - 1] + (a[i] > m);
    if(a[i] == m)
      p = i;
  }
  for(int i = 1; i < p; i++)
    mp[p1[i] - p2[i]]++;
  for(int i = p; i <= n; i++)
    ans += mp[p1[i] - p2[i]] + mp[p1[i] - p2[i] + 1];
  cout << ans;
  return 0;
}
posted @ 2026-02-02 21:27  tangtianyao0123  阅读(1)  评论(0)    收藏  举报