题目描述:
给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
题解:因为中位数是位于一个序列中最中间的数字了,所以这个序列左边的数的个数是要等于它右边的个数的,所以我们可以把序列给替换成1和-1的序列,大于这个数的换成1,小于这个数的换程-1,这样的话我们可以分为三种情况来统计这个序列的个数
1.0到pos-1
2.pos+1到n-1
3.0到pos到n-1
(因为序列必须是连续的)第一种情况好统计,也就是说我们找一个sum来记录从pos-1位置往前走,每走一步加一下(已经改过的a[i]),当sum=0的时候就意味着大于m的数和小于m的数相等,故sum++;
当然第二种情况也可以由上面的那种方法进行统计。
但是最后一种情况怎么统计呢,我们可以在统计第一种情况的时候顺便统计cnt[maxn+sum]++,这个代表着什么呢,也就是说这个后缀和为maxn+sum的总序列数到底有多少个,故,我们在进行第三种情况的统计时,当maxn-sum,也就是说,假设走到某个位置,此时从pos到这个位置sum的相反数的序列情况有几种,(这个相反数的序列总数在第一种情况已经统计过了),所以我们在统计第二种情况的时候顺便可以统计第三种情况了,ans+=cnt[maxn-sum],为什么时-sum,因为我们要大于m的数正好等于小于m的数,才能保证m是中位数。
#pragma GCC optimize(3,"Ofast","inline") #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <math.h> #include <string> #include <list> #include <set> #include <map> #include <queue> #include <stack> #include <algorithm> #include <stdlib.h> #include <vector> const int maxn = 1e5+10; const int MaxN = 0x3f3f3f3f; const int MinN = 0xc0c0c00c; typedef long long ll; const int mod = 1000007; using namespace std; int a[maxn]; int cnt[maxn*2]; int main() { int n,m,pos; cin>>n>>m; for(int i=0;i<n;i++){ scanf("%d",&a[i]); if(a[i]>m) a[i]=1; else if(a[i]<m) a[i]=-1; else a[i]=0,pos=i; } int sum=0,ans=0; for(int i=pos-1;i>=0;i--){ sum+=a[i]; if(sum==0) ans++; cnt[maxn+sum]++; } sum=0; for(int i=pos+1;i<n;i++){ sum+=a[i]; if(sum==0) ans++; ans+=cnt[maxn-sum]; } cout<<ans+1<<endl; return 0; }