也是神奇的题目。。
首先看到这道题目,很明显我们需要线性算法
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 ",ans); fclose(stdin); fclose(stdout); }