题意

给定 1 1 1 1 ∼ n 1\sim n 1n 的排列和一个数 b b b,保证 b b b 1 ∼ n 1\sim n 1n 中的一个整数,求以 b b b 为中位数的长度为奇数的连续子序列个数。

分析

首先想到的是暴力求解,枚举所有包含 b b b 的长度为奇数的连续子序列,记录答案,这个思路明显会超时。

进一步优化,考虑一个长度为奇数的连续子序列如果它的中位数是 b b b,那么其中大于 b b b 的数和小于 b b b 的数一定是一样多的。以 b b b 为中心,分别向前后枚举,如果大于 b b b 的数和小于 b b b 的数数量相等,就记录答案,这个思路比上一个有所优化,但依然无法通过所有测试点。

再进一步优化,只从 b b b 向前枚举, b b b 之后的部分进行预处理,并且不记录大于 b b b 的数和小于 b b b 的数的数量,而是记录大于 b b b 的数和小于 b b b 的数的数量差,如果 b b b 之前的数量差与 b b b 之后的数量差相加为零,则这一部分能够构成一个以 b b b 为中位数的长度为奇数的连续子序列,这个思路可以通过所有测试点。

代码

#include<bits/stdc++.h>
using namespace std;
long long n,m,a[1000005],t,xc,dc,ans,xxc,ddc;
map<long long,long long>p;
int main()
{
   
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
   
		cin>>a[i];
		if(a[i]==m)t=i;//找到m的位置。
	}
	xxc=ddc=xc=dc=0;
	for(int i=t+1;i<=n;i++)//预处理
	{
   
		if(a[i]<m)xxc++;
		else ddc++;
		p[xxc-ddc]++;
	}
	for(int i=t;i>0;i--)
	{
   
		if(i!=t)
		{
   
			if(a[i]<m)xc++;
			else dc++;
		}
		if(xc==dc)ans++;
		ans=ans+p[-1*(xc-dc)];
	}
	cout<<ans;
	return 0;
}