牛客补题 骚区间

骚区间

题目链接

题意

给一个数组,问有多少个连续子序列l ~ r满足 a[l]是这个区间的次小值a[r]是这个区间的次大值。

题解

刚开始上去没什么想法,但是后来发现
对于一个数,
1、当这个数是左边界时,从他右边比他大的第一个开始到比他大的第二个数之间的数都可能是右边界。
2、当这个数是右边界时,他的左边比他小的第一个数到比他小的第二个数之间都可能是左边界。
但是这两种情况混一下我就不知道该怎么处理了。。。
可能见的题目还是少,比较菜把。
可以用树状数组维护。
树状数组里放的是有多少值当左边界的时候,右边界可能取得到这个位置。。
可能有点绕。。说不清,或许是我理解的不太到位。
设当前位置 i 是左边界的时候x ~ y 的值可能是右边界。然后 遍历到x的时候 把i 这个位置加进去,遍历到y的时候把i 这个位置减去,像差分一样。
然后 统计的时候就可以按编号2 中的值统计,也就是 当前位置是右边界时,查询左边界有多少 然后加进去。
代码:

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <cmath>
#include <set>
#include <cstring>
#include <string>
#include <bitset>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void wenjian(){freopen("concatenation.in","r",stdin);freopen("concatenation.out","w",stdout);}
void tempwj(){freopen("hash.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second > b.second;}};
int lb(int x){return  x & -x;}
typedef set<int>::iterator SI;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 9901;
 
const int M = 10000007;
const int maxn = 1e6+5;
int a[maxn];
int pos[maxn];
set<int> ss;
std::vector<int> vl[maxn];
std::vector<int> vr[maxn];
int pl[maxn];
int pr[maxn];
int tree[maxn];
int n;
void add(int x, int num)
{
	while(x <= n)
	{
		tree[x] += num;
		x += lb(x);
	}
}

int query(int x)
{
	int ans =0 ;
	while(x > 0)
	{
		ans += tree[x];
		x -= lb(x);
	}
	return ans;
}

int main()
{
	
	scanf("%d",&n);
	for (int i = 1; i <= n; i ++ )
	{
		scanf("%d",&a[i]);
		pos[a[i]] = i;
	}
	for (int i = 1; i <= n; i ++ )
	{
		ss.insert(pos[i]);
		SI x = ss.upper_bound(pos[i]);
		if(x != ss.end())
		{
			vl[*x].pb(pos[i]);
			x ++ ;
			if(x != ss.end())
			{
				int t = *x;
				vr[t - 1].pb(pos[i]);
			}
		}
	}
	ss.clear();
	for (int i = n; i >= 1; i -- )
	{
		ss.insert(-pos[i]);
		SI x = ss.upper_bound(-pos[i]);
		if(x != ss.end())
		{
			pr[pos[i]] = - *x;
			x ++ ;
			if(x != ss.end())
			{
				pl[pos[i]] = - *x + 1;
			}
		}
	}

	ll ans = 0;
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 0; j < vl[i].size(); j ++ )
		{
			add(vl[i][j],1);
		}
		// printf("11111111111111");
		// printf("%d %d %d\n",i,pl[i],pr[i]);
		ans += query(pr[i]) - query(pl[i] - 1);
		for (int j = 0; j < vr[i].size(); j ++ )
		{
			add(vr[i][j],-1);
		}
	}
	printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值