[CQOI2009] 中位数 (前缀和)

[CQOI2009] 中位数

题目描述

给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。

输入输出格式

输入格式:

第一行为两个正整数n和b,第二行为1~n的排列。

【数据规模】

对于30%的数据中,满足n≤100;

对于60%的数据中,满足n≤1000;

对于100%的数据中,满足n≤100000,1≤b≤n。

输出格式:

输出一个整数,即中位数为b的连续子序列个数。

输入输出样例

输入样例#1:

7 4
5 7 2 4 3 1 6

输出样例#1:

4

Solution

首先题目有一个隐含的性质,因为是排列,而又保证\(b\)出现过,所以\(b\)在这\(n\)个数中有且只有一个

这道题可以\(O(n^2)\)通过统计前缀和拿到60分,我们只关心相对大小,所以可以把大于\(b\)的设成1,小于\(b\)的设成-1

因为是中位数,所以前后的数字个数必定相等,在我们设了1/-1之后,又多了一个条件,即左边的数字的和+右边数字的和=0(想一想就知道了)

那么我们就可以先统计一边的前缀和的个数,再在另一边一边计算前缀和一边统计答案,因为可能会有负数,所以有两种方法,一种是把数组平移,一种是开\(map\)

具体结合代码(画一下)就懂了

Code

#include<bits/stdc++.h>
#define in(i) (i=read())
#define il extern inline
#define rg register
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define lol long long
using namespace std;

const lol N=1e5+10;

lol read() {
    lol ans=0, f=1; char i=getchar();
    while (i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while (i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+(i^48), i=getchar();
    return ans*f;
}

int n,m,sum,ans,pos,a[N];
map<int,int>cnt;

int main()
{
    in(n), in(m);
    for (rg int i=1;i<=n;i++) {
        in(a[i]);
        if(a[i]>m) a[i]=1;
        else if(a[i]==m) a[i]=0, pos=i;
        else a[i]=-1;
    }
    for (int i=pos;i<=n;i++)
        sum+=a[i], cnt[sum]++;
    sum=0;
    for (int i=pos;i>=1;i--)
        sum+=a[i], ans+=cnt[-sum];
    cout<<ans<<endl;
}
posted @ 2018-11-06 21:37  real_l  阅读(479)  评论(0编辑  收藏  举报