median(NOIP模拟赛Round 3)

也是神奇的题目。。

原题传送门

首先看到这道题目,很明显我们需要线性算法

1、前缀和+统计

2、DP+统计

对于第一种算法,我们可以对于任何一个a[i]对b进行比较,如果大于b标上1,等于b标上0,小于b标上-1;

以0为中介,前缀和前后展开统计答案即可。。学长的神奇做法,不讲。。

第二种算法啊,,

其实和第一种算法差不多

首先我们找到中位数b

对其左边和右边进行搜索

对于右边if(a[i]>a[i-1])f[i]=f[i-1]+1;else f[i]=f[i-1]-1;

左边同上,不过i-1改为i+1。。(水~)

然后我们很快会想到如果我们发现f[a[p](a[p]=b)]=f[i]=0时,答案肯定要+1。。这个很好理解吧。。

那么我们发现其实中位数序列的起点或终点不一定一定是a[p]

所以我们再对于每一次出现的f[i]做一下统计

即if(f[i])tot[0][0][f[i]]++;else tot[0][1][-f[i]]++;(统计另一边的时候要写成f[i]>=0!注意!这非常重要!,因为前段的0归为负数,后端的0就要归为正数!,否则统计会出错!

第一个维度是记录在a[p]左还是右边,第二个维度是记录是否为负数。。由于C++没有负数数组,所以不加会RE。

然后判断的时候如果发现(tot[0][0][i]&&tot[1][1][i])||(tot[0][1][i]&&tot[1][0][i])

显然我们的答案就是左边端点数*右边端点数

然后就是代码啦!

#include<iostream>
#include<cstdio>
using namespace std;
int n,b,now;
unsigned long long ans;
int a[100001];
int f[100001];
int cnt[3][2][100001];
int main(){
    freopen("median.in","r",stdin);
    freopen("median.out","w",stdout);
    scanf("%d%d",&n,&b);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i]==b)now=i;
    }
    for(int i=now+1;i<=n;i++)
    {
        if(a[i]>b)f[i]=f[i-1]+1;
        else f[i]=f[i-1]-1;
        if(f[i]>=0)cnt[1][0][f[i]]++;
        else cnt[1][1][-f[i]]++;
    }
    for(int i=now-1;i>=1;i--)
    {
        if(a[i]>b)f[i]=f[i+1]+1;
        else f[i]=f[i+1]-1;
        if(f[i]>0)cnt[2][0][f[i]]++;
        else cnt[2][1][-f[i]]++;    
    }
    for(int i=1;i<=n;i++)if(f[i]==0)ans++;
    for(int i=0;i<=n-1;i++)
    {
        if(cnt[1][0][i]&&cnt[2][1][i])ans+=cnt[1][0][i]*cnt[2][1][i];
        if(cnt[1][1][i]&&cnt[2][0][i])ans+=cnt[1][1][i]*cnt[2][0][i];
    }
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout); 
}

 

posted @ 2017-05-24 21:34  ghostfly233  阅读(223)  评论(0编辑  收藏  举报