【BZOJ1303】思维题

题意还是很简单

题意:
在全排列中找长度为奇数的连续子序列,且中位数是b,问一共可以找到多少个连续子序列。

分析:
既然是全排列,我们就不需要考虑有多个b的情况了。
那么我们就可以把b的位置记录下来,然后往两边扩展。

设b的位置为pos。
lmin[i]表示[i,pos-1]中比b小的有多少个
lmax[i]表示[i,pos-1]中比b大的有多少个
rmin[j]表示[pos+1,j]中比b小的有多少个
rmax[j]表示[pos+1,j]中比b大的有多少个

那么在区间[i,j]中,满足条件的就是这三种情况
lmin[i]+rmin[j]==lmax[i]+rmax[j] (i<pos<j)
lmin[i]==lmax[i] (i<pos)
rmin[j]==rmax[j] (j>pos)

后两种情况我们在处理数组的时候就可以统计到。
但是第一种情况怎么办呢?
其实也很简单。

我们变一下形,lmin[i]-max[i]==-(rmin[j]-rmax[j])

显然我们可以先预处理出来他们的个数,然后直接乘起来(乘法原理)

看代码就完事了

#include <bits/stdc++.h>
#define sc(n) scanf("%d",&n)
#define pt(n) printf("%d\n",n)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define vi vector<int>
#define vl vector<long long>
#define pb push_back
using namespace std;
const int maxn = 1e5+7;
int a[maxn];
int lmin[maxn],lmax[maxn],rmin[maxn],rmax[maxn];
map<int,long long> t1,t2;
int n,b;
int main()
{
	scanf("%d%d",&n,&b);
	int pos = 0;
	long long ans = 0;
	rep(i,1,n)
	{
		sc(a[i]);
		if(a[i]==b) pos = i;
	}
	for(int i=pos-1;i>=1;i--)
	{
		lmin[i] = lmin[i+1];
		lmax[i] = lmax[i+1];
		if(a[i]<b) lmin[i]++;
		else lmax[i]++;
		if(lmin[i]==lmax[i]) ans++;
	}
	for(int i=pos+1;i<=n;i++)
	{
		rmin[i] = rmin[i-1];
		rmax[i] = rmax[i-1];
		if(a[i]<b) rmin[i]++;
		else rmax[i]++;
		if(rmin[i]==rmax[i]) ans++;
	}
	for(int i=1;i<pos;i++)
	{
		t1[lmin[i]-lmax[i]]++;
	}
	for(int i=pos+1;i<=n;i++)
	{
		t2[rmin[i]-rmax[i]]++;
	}
	for(int i=-n;i<=n;i++)
	{
		ans += t1[i]*t2[-i];
	}
	printf("%lld\n",ans+1);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值