【洛谷P1627】 【CQOI2009】中位数
问题描述
有一个长度为N的数列{A1,A2,...,AN},这N个数字恰好是1...N的一个排列。你需要求出这个序列有多少个子序列{Ai,Ai+1,...,Aj}满足:
1、i≤j且j-i+1为奇数。
2、这个序列的中位数为B。
例如{5,1,3}的中位数为3。
输入格式
输入文件第一行包含两个正整数N和B,满足1≤N≤100000且1≤B≤N。
第二行包含N个整数Ai。
输出格式
输出文件仅包含一个整数,为满足条件的子序列个数。
样例输入
7 4
5 7 2 4 3 1 6
样例输出
4
题解
如果B是一个序列的中位数,那么这个序列中比B大的数的个数和比B小数的个数相等。事实上,我们不需要知道这些数的具体数值,只要知道这些数和B的大小关系就够了。
把比B大的数记为1,比B小的数记为-1,分别计算以B为右端点的后缀和lsum和以B为左端点的前缀和rsum,对于任意一个子序列的左端点l和右端点r,当lsum[l]+rsum[r]==0时满足条件。
设lnum[i]表示lsum[j]==i的lsum的个数,rnum[i]表示rsum[j]==i的rsum的个数,根据乘法原理,满足条件的子序列的个数为∑lnum[i]*rnum[-i]
注意到lnum和rnum的下标可能为负,数组整体右移。
1 #include <cstdio> 2 int n,B,a[100005],lsum[100005],rsum[100005],lnum[200005],rnum[200005],ans; 3 int main() 4 { 5 int i,j,p,l,r; 6 scanf("%d%d",&n,&B); 7 for (i=1;i<=n;i++) 8 scanf("%d",&a[i]); 9 for (p=1;p<=n && a[p]!=B;p++); 10 lnum[n]=rnum[n]=1; 11 for (i=p-1;i>=1;i--) 12 lsum[i]=a[i]>B?1:-1, 13 lsum[i]+=lsum[i+1], 14 lnum[lsum[i]+n]++; 15 for (i=p+1;i<=n;i++) 16 rsum[i]=a[i]>B?1:-1, 17 rsum[i]+=rsum[i-1], 18 rnum[rsum[i]+n]++; 19 for (i=-n;i<=n;i++) 20 ans+=lnum[i+n]*rnum[-i+n]; 21 printf("%d",ans); 22 return 0; 23 }