BZOJ1303: [CQOI2009]中位数图

【传送门:BZOJ1303


简要题意:

  给出一条n个数的序列,保证序列里的数为1~n,且互不相等,再给出一个数b,求出以b为中位数的长度为奇数的子序列个数


题解:

  显然要你求以b为中位数的长度为奇数的子序列,其实就是求包含b的,以b为中位数的子序列,因为序列中的数互不相等,而且求的是奇数长度

  那我们把序列中大于b的,改值为-1,小于b的,改值为1,等于b,就是b的位置,直接记录b的位置

  那显然就是一个求前缀和问题,首先定义L,R数组,分别表示以b所在的位置向前后延伸所得到的和的个数

  简单地用样例来说明:

   7   4
   5   7   2   4   3   1   6

  -1  -1   1   0   1   1  -1

  b的位置是4,向前后延伸,先向前,向前一位后,L[1]++,向前两位后,L[1+(-1)=0]++,向前三位后,L[0-1=-1]++,就是这样,R数组的求法也是这样

  那么答案就是ΣL[i]*R[-i](0<=i<=n)

  那么因为C++中的数组是访问不到负数的,所以就把得到的和加上n再记录到L数组,这样求解的时候就是ΣL[i+n]*R[n-i](0<=i<=n)


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int a[110000],s[110000];
int mid[110000];
int L[210000];
int R[210000];
int main()
{
    int n,b;
    scanf("%d%d",&n,&b);
    int x=0;
    memset(s,0,sizeof(s));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i]>b) s[i]=-1;
        if(a[i]<b) s[i]=1;
        if(a[i]==b) x=i;
    }
    int sum=0;
    L[n]=1;R[n]=1;
    for(int i=x-1;i>=1;i--)
    {
        sum+=s[i];
        L[sum+n]++;
    }
    int ans=0;
    sum=0;
    for(int i=x+1;i<=n;i++)
    {
        sum+=s[i];
        R[sum+n]++;
    }
    for(int i=-n;i<=n;i++)
    {
        ans+=L[i+n]*R[n-i];
    }
    printf("%d\n",ans);
    return 0;
}

 

 

 

posted @ 2017-09-27 13:54  Star_Feel  阅读(255)  评论(0编辑  收藏  举报