bzoj 1303

很容易想到把大于b的数赋值为1,小于b的数赋值为-1。

做一遍前缀和。

于是问题变成求sum[j]-sum[i]=0 (0<=i<w && j>=w)

于是直接按照b所在位置划分成两段,分别用桶统计sum某值出现的次数,注意sum数组中的数可能会出现负数,于是要加上一个n防止访问负的下标。

最后用乘法原理统计答案即可。

#include"cstdio"
#include"cctype"
int read()
{
    int c,x=0; while(!isdigit(c=getchar()));
    while(x=x*10+c-'0',isdigit(c=getchar()));
    return x;
}
int a[100001],s[100001],h[200001],t[200001];
int main()
{
    int n=read(),b=read(),w;
    long long ans=0;
    for(int i=1; i<=n; i++)
    {
        a[i]=read();
        if(a[i]==b) w=i,a[i]=0;
        else if(a[i]>b) a[i]=1;
        else a[i]=-1;
        s[i]=s[i-1]+a[i];
    }
    for(int i=0; i<w; i++) h[s[i]+n]++;
    for(int i=n; i>=w; i--) t[s[i]+n]++;
    for(int i=0; i<=n*2; i++) ans+=(long long)h[i]*t[i];
    printf("%lld",ans);
    return 0;
}

设b所在位置为w。 

其实也可以按照w划分为两段,然后往前做一次后缀和,往后做一次前缀和。

记s1[i]表示从i往后一直到w的和。

记s2[i]表示从i往前一直到w的和。

于是求s1[i]+s2[j]=0 (1<=i<=w && w<=j<=n)

用两个桶分别统计一下s1和s2中某值出现的个数,还是要加上n防止访问负的下标。

最后还是乘法原理统计答案。

#include"cstdio"
#include"cctype"
int read()
{
    int c,x=0; while(!isdigit(c=getchar()));
    while(x=x*10+c-'0',isdigit(c=getchar()));
    return x;
}
int a[100001],s1[100001],s2[100001],h[200001],t[200001];
int main()
{
    int n=read(),b=read(),w;
    long long ans=0;
    for(int i=1; i<=n; i++)
    {
        a[i]=read();
        if(a[i]==b) w=i,a[i]=0;
        else if(a[i]>b) a[i]=1;
        else a[i]=-1;
    }
    h[n]=t[n]=1;
    for(int i=w-1; i>=1; i--) s1[i]=s1[i+1]+a[i],h[s1[i]+n]++;
    for(int i=w+1; i<=n; i++) s2[i]=s2[i-1]+a[i],t[s2[i]+n]++;
    for(int i=0; i<=n*2; i++) ans+=(long long)h[i]*t[2*n-i];
    printf("%lld",ans);
    return 0;
}

 

posted @ 2018-01-23 20:34  TrassBlose  阅读(80)  评论(0编辑  收藏  举报