【洛谷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 }

 

posted @ 2018-10-25 19:46  SAKURA12  阅读(205)  评论(0编辑  收藏  举报