这题看了别人的博客,看的我一脸懵逼。
思路:很巧秒的转换,我们把<= m 数记为-1, >m的数 记为1, 求其前缀和, 我们将问题转变成求以> m 的数作为中位数的区间个数,
答案就变为ans(m-1) - ans(m ),我们可以用上面求得的前缀用bit就能求出答案。
我特么还不知道是这样写的么,我是不知道怎么用前缀。然后纠结了半天,是咱的基础不好。
所以重点是怎么用bit位来处理前缀和呢?
只可意会不可言传
5 4
1 4 5 60 4
首先传个 当 k =4 时
就是 -1 -2 -1 0 -1
首先 知道 前缀和为负数的中卫肯定是<=4为正数的一定是>4
结点图如下,至于8以上的结点就不画了,画了也没用前缀合最大值不会超过8;
首先 0 +n+1=5 (初始值都为-1) 和这个以后的结点值 ++;
也就是 6 8结点 都加1;
下一个值 -1
把所有 -1+n=4 一下的结点 的值都加起来。
然后 把 小于 -1+n 的结点和都加一,也就是 4 8都加1;
后面的都是同样的道理
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int N=2*maxn;
int n,m,a[maxn];
int v[N];
void jl(int x) {
for(int i=x; i<N; i+=i&-i) {
//这个是来保留的前缀和,假如 当前位置的前缀合 是 -1 ,因为可能出现负数的情况所以加上 n
// 然后比 -1 + n 向上跳转的结点 都加上1
v[i]++;
}
}
long long s(int x) {
long long sum=0;
for(int i=x; i>0; i-=i&-i) {
sum+=v[i];
// 计算前缀和,从上往下加,这样会把小于当前结点的值都加起来。
}
return sum;
}
long long cal(int k){
memset(v,0,sizeof(v));
jl(n+1);
int dp=0;
long long sum=0;
for(int i=0;i<n;i++){
if(a[i]<=k)dp-=1;
else dp+=1;
sum+=s(dp+n);
jl(dp+n+1);
}
return sum;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)scanf("%d",&a[i]);
cout<<cal(m-1)-cal(m)<<endl;
return 0;
}