1249 近似有序区间 51nod

题目链接

线段树加一点剪枝, 固定每个点为左端点,找出做最大近似区间,然后在这个区间中找有多少个满足题意的右端点,线段树查询最大值。倒着扫描。

参考

代码

#include <bits/stdc++.h>
using namespace std;
#define rs rt << 1|1
#define ls rt << 1
const int N = 50000 + 5; 
int tree[N << 2];
int a[N];
void build(int l, int r, int rt){
	if(l == r){
		tree[rt] = a[l];
		return ;
	}

	int mid = (l + r) >> 1;
	build(l, mid, ls);
	build(mid + 1, r ,rs);
	tree[rt] = max(tree[ls], tree[rs]);
}

int query(int l ,int r ,int rt, int L, int R , int val){
	if(l == r){
		if(a[l] >= val){
			return l;
		}
		return 0;
	}

	int mid = l + r >> 1, ans = 0;
	if(tree[rt] >= val){
		if(L <= mid){
			ans = query(l ,mid,ls, L ,R , val);
		}
		if(ans == 0 && R > mid){
			ans = query(mid + 1, r ,rs , L ,R ,val);
		}
	}
	return ans;
}
int vis[N], sum[N];
int main()
{
	int n;
	scanf("%d",&n);

	for(int i = 1; i <= n; i ++){
		scanf("%d",&a[i]);
	}	

	build(1, n , 1);

	for(int i = n; i >= 1; i --){
		if(a[i] > a[i + 1]){
			vis[i] = i;
			sum[i] = 1;
		}
		else {
			vis[i] = vis[i + 1];
			sum[i] = sum[i + 1] + 1;
			int mx = a[vis[i]];
			int l = vis[i] + 1, r = l;
			while(a[i] <= a[r]){//确定最大区间
				r = vis[r] + 1;
			}
			r -- ;

			while(l <= r){
				mx = query(1, n , 1 , l , r ,mx); //找到区间内从左开始找依次增大的值的位置
				if(mx){
					sum[i] ++;
				}
				else break;
				vis[i] = mx; 
				l = mx + 1;
				mx = a[mx];
			}
		}
	}
	int ans = 0;
	for(int i = 1; i <= n; i ++){
		ans += sum[i];
	}
	cout << ans << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值