涉及知识点:已知中位数的题的一般思路
题目描述
给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
输入描述:
第一行为两个正整数n和b ,第二行为1~n 的排列。
输出描述:
输出一个整数,即中位数为b的连续子序列个数。
示例1
输入
复制7 4 5 7 2 4 3 1 6
7 4 5 7 2 4 3 1 6
输出
复制4
4
备注:
对于30%的数据中,满足 n≤100n \le 100n≤100; 对于60%的数据中,满足 n≤1000n \le 1000n≤1000; 对于100%的数据中,满足 n≤100000,1≤b≤nn \le 100000,1 \le b \le nn≤100000,1≤b≤n。
想法:
没有想法,不会(微笑)。好吧,实现不出来。就是标记一下目标元素的下标,然后分两种情况:一是子序列以目标元素之前的元素开头到目标元素,然后向后延展,二是子序列以目标元素开头,向后延展。这里实现不出来,想不到。
2.3:
答疑:
这题不会写正常,我看他用的方法还是我想不到的。我们关心的不是具体的值,而是这个数字比目标值(中位数)大还是比目标值小。所以我们把比目标值大的置为1,比目标值小的置为-1,目标值置为0。然后以0(目标值)为中心,分别向两边延申,算子串和(即子串中各个抵消后,大于0或小于0的个数)。最后把0的两端值相加为0(互为相反数)的配对,看有多少对可以配对。以后遇到中位数的题都可以先往这个方向想。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,b;
int a[100010];
int r[300010],l[300010];
int main(){
int sign;
scanf("%d%d",&n,&b);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>b) a[i]=1;
else if(a[i]<b) a[i]=-1;
else if(a[i]==b) {
a[i]=0;
sign=i;//记录中位数的下标
}
}
for(int i=sign-1;i>0;i--){//后缀和(目标值左端)
a[i]=a[i]+a[i+1];
l[a[i]+100000]++;//下标平移,因为下标可能是负数
}
for(int i=sign+1;i<=n;i++){//前缀和(目标值右端)
a[i]=a[i]+a[i-1];
r[a[i]+100000]++;
}
int ans=0;
for(int i=0;i<=200000;i++){
ans+=l[i]*r[200000-i];//左右匹配
}
ans+=l[100000]+r[100000]+1;//左右两端为0的值还可以直接到中位数构成子串,加一是因为直接一个中位数也满足条件
cout<<ans;
}