题目描述
给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
输入描述:
第一行为两个正整数n和b ,第二行为1~n 的排列。
输出描述:
输出一个整数,即中位数为b的连续子序列个数。
输入
7 4
5 7 2 4 3 1 6
输出
4
备注:
对于30%的数据中,满足 n≤100; 对于60%的数据中,满足 n≤1000; 对于100%的数据中,满足 n≤100000,1≤b≤n。
———————————————————————————————————————————
题解:
由于所给数据是1-n的排列,所以每个数一定只出现一次,并且我们所找的连续子数列是奇数个,所以中位数一定出现在最中间,左边小于它的数与右边大于它的数的个数相等。
我们要让b成为中位数,只需要保证左边小于它的数与右边大于它的数的个数一致即可,不需要关注这些数的具体数值是什么,一般情况下常常把大于它的数写成1,小于它的数写成-1,把它本身写为0,它本身即使一种满足题意的解法。由于题目给出1≤b≤n,所以不需要再去讨论b,可以直接把ans初始化为1。
然后再去寻找满足题意的大小为奇数的连续子序列有多少个,由于左边小于它的数与右边大于它的数的个数相等,一定是偶数,所以能保证满足题意的连续子序列一定是奇数个,不需要再去考虑这个条件。我们最后只需要让连续子序列的和变为0,有三种找法,从b往左找,从b往右找,从b往左右两边发散。首先往左边找,记录下左边序列的后缀和,如果有后缀和为0的情况,ans可以直接加1;然后往右找,算右边序列的前缀和,如果为0,ans+1;第三种找法我们就需要看右边序列的前缀和和左边序列的后缀和中是否有和为0的情况,所以我们可以在往左边找的时候用map记录后缀和,然后在往右找时,看这种情况是否存在。
#include<bits/stdc++.h>
using namespace std;
const int Max=1e5+5;
int a[Max];
map<int,int>m;
int main(){
ios::sync_with_stdio(false);
cout.tie(0);cin.tie(0);
int n,b,i,pos,ans=1,sum=0;
cin>>n>>b;
for(i=0;i<n;i++){
cin>>a[i];
if(a[i]>b) a[i]=1;
else if(a[i]<b) a[i]=-1;
else{
a[i]=0;
pos=i;
}
}
//左边的后缀和
for(i=pos-1;i>=0;i--){
sum+=a[i];
m[sum]++;
if(sum==0){
ans++;
}
}
//右边的前缀和
sum=0;
for(i=pos+1;i<n;i++){
sum+=a[i];
if(sum==0){
ans++;
}
if(m[-sum]!=0){
ans+=m[-sum];
}
}
cout<<ans<<endl;
return 0;
}