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;
}