#3487. 第三题(disanti)

题目描述

这是第三题,有趣的是这套题都是送分题。

给出一个 1 1 1 到 N N N 的排列,你需要求出有多少个区间 [L,R] [L,R] [L,R] ,满足这个区间的值是连续的。比如 2,3,1 2,3,1 2,3,1 是一个合法的区间,而 3,1 3,1 3,1 不是。
输入格式

第一行一个整数 N N N 。

接下来 N N N 个正整数,第 i i i 个数表示排列的第 i i i 个数。
输出格式

一行一个整数表示答案。
样例
样例输入

5
1 2 3 5 4

样例输出

12

数据范围与提示

对于 30% 30 % 30% 的数据, N≤103 N \le 10^3 N≤103 。

对于另外 5% 5 % 5% 的数据, 序列中相邻两个元素的差的绝对值不超过 1 1 1 。

对于另外 25% 25 % 25% 的数据, 序列中相邻两个元素的差的绝对值不超过 4 4 4 。

对于 100% 100 % 100% 的数据, 1≤N≤3×105 1 \le N \le 3 \times 10^5 1≤N≤3×105 ,保证给出的是一个排列。
来源

2017年12月成都七中集训
题解:
首先,对于一个合法序列,可知

r-l=Max-Min;

Max是区间最大值,Min是区间最小值,r是区间右端点,l是区间左端点。
变形可得

r=Max-Min+l;

所以可知,对于每一个右端点r,需要维护每一个左端点到r的最大值和最小值。
于是我们想到了单调栈。所以维护最大最小就很可行。
但怎么快速区间修改值呢?
于是我们想到线段树。
lazy标记和传值细节详情请见代码
代码来源@zhanghongyu

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#define N 300005
using namespace std;
int n,a[N];
inline int read() {
	int x,f=1;char ch;
	while (!isdigit(ch=getchar())) if (ch=='-') f=-1;
	for (x=ch-48;isdigit(ch=getchar());x=(x<<1)+(x<<3)+ch-48);
	return f*x;
}
struct segement
{
	int l,r;long long maxn,minn,sum,cnt,lazy_max,lazy_min;
	long long minn_cnt,maxn_cnt;
}t[N<<3];
//maxn=max+l
//minn=-min+l
struct node
{
	long long val;
	int l;
};
node upp[N],dow[N];
int upp_top=0,dow_top=0;
void uprate(int p){
	t[p].cnt=0;t[p].minn_cnt=0;t[p].maxn_cnt=0;
	t[p].maxn=min(t[p*2].maxn,t[p*2+1].maxn);
	t[p].minn=min(t[p*2].minn,t[p*2+1].minn);
	if(t[p].minn==t[p*2].minn)t[p].minn_cnt+=t[p*2].minn_cnt;
	if(t[p].minn==t[p*2+1].minn)t[p].minn_cnt+=t[p*2+1].minn_cnt;
	if(t[p].maxn==t[p*2].maxn)t[p].maxn_cnt+=t[p*2].maxn_cnt;
	if(t[p].maxn==t[p*2+1].maxn)t[p].maxn_cnt+=t[p*2+1].maxn_cnt;
	t[p].sum=min(t[p*2].sum,t[p*2+1].sum);
	if(t[p].sum==t[p*2].sum)t[p].cnt+=t[p*2].cnt;
	if(t[p].sum==t[p*2+1].sum)t[p].cnt+=t[p*2+1].cnt;
}
void build(int p,int l,int r){
	t[p].l=l;t[p].r=r;
	if(l==r){
		t[p].sum=l;
		t[p].maxn=l;t[p].minn=l;
		t[p].minn_cnt=1;t[p].maxn_cnt=1;
		t[p].cnt=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(p*2,l,mid);build(p*2+1,mid+1,r);
	uprate(p);
}
void spread(int p){
	if(t[p].lazy_max){
		t[p*2].sum=t[p*2].minn+t[p].lazy_max;
		t[p*2].cnt=t[p*2].minn_cnt;
		t[p*2].maxn=t[p].lazy_max+t[p*2].l;
		t[p*2].maxn_cnt=1;
		t[p*2+1].sum=t[p*2+1].minn+t[p].lazy_max;
		t[p*2+1].cnt=t[p*2+1].minn_cnt;
		t[p*2+1].maxn=t[p].lazy_max+t[p*2+1].l;
		t[p*2+1].maxn_cnt=1;
		t[p*2].lazy_max=t[p].lazy_max;
		t[p*2+1].lazy_max=t[p].lazy_max;
		t[p].lazy_max=0;
	}
	if(t[p].lazy_min){
		t[p*2].sum=t[p*2].maxn-t[p].lazy_min;
		t[p*2].cnt=t[p*2].maxn_cnt;
		t[p*2].minn=t[p*2].l-t[p].lazy_min;
		t[p*2].minn_cnt=1;
		t[p*2+1].sum=t[p*2+1].maxn-t[p].lazy_min;
		t[p*2+1].cnt=t[p*2+1].maxn_cnt;
		t[p*2+1].minn=t[p*2+1].l-t[p].lazy_min;
		t[p*2+1].minn_cnt=1;
		t[p*2].lazy_min=t[p].lazy_min;
		t[p*2+1].lazy_min=t[p].lazy_min;
		t[p].lazy_min=0;
	}
}

void change_max(int p,int l,int r,long long maxn){
	if(t[p].l>=l&&t[p].r<=r){
		spread(p);
		t[p].sum=t[p].minn+maxn;
		t[p].cnt=t[p].minn_cnt;
		t[p].maxn=maxn+t[p].l;
		t[p].maxn_cnt=1;
		t[p].lazy_max=maxn;
		return ;
	}
//	if(t[p].l!=t[p].r)
	spread(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid)change_max(p*2,l,r,maxn);
	if(r>mid)change_max(p*2+1,l,r,maxn);
	uprate(p);
}
void change_min(int p,int l,int r,long long minn){
	if(t[p].l>=l&&t[p].r<=r){
		spread(p);
		t[p].sum=t[p].maxn-minn;
		t[p].cnt=t[p].maxn_cnt;
		t[p].minn=t[p].l-minn;
		t[p].minn_cnt=1;
		t[p].lazy_min=minn;
		return ;
	}
//	if(t[p].l!=t[p].r)
	spread(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid)change_min(p*2,l,r,minn);
	if(r>mid)change_min(p*2+1,l,r,minn);
	uprate(p);
}
pair<long long, long long> ask(int p,int r){
	if(t[p].r<=r){
		return make_pair(t[p].sum,t[p].cnt);
	}
//	if(t[p].l!=t[p].r)
	spread(p);
	int mid=(t[p].l+t[p].r)>>1;
	pair<long long, long long> ls,rs;
	ls=ask(p*2,r);
	if(r>mid)rs=ask(p*2+1,r);
	else return ls;
	pair<long long, long long> ans;
	ans.first=min(ls.first,rs.first);
	ans.second=0;
	if(ans.first==ls.first)ans.second+=ls.second;
	if(ans.first==rs.first)ans.second+=rs.second;
	return ans;
}
int main()
{
	n=read();
	for(int i=1;i<=n;++i){
		a[i]=read();
	}
	build(1,1,n);
	long long ans=0;
	for(int i=1;i<=n;++i){
		int upp_l=i;
		while(upp_top>0&&upp[upp_top].val>a[i]){
			upp_l=upp[upp_top].l;upp_top--;
		}
		upp[++upp_top].val=a[i];upp[upp_top].l=upp_l;
		change_min(1,upp_l,i,a[i]);
		int dow_l=i;
		while(dow_top>0&&dow[dow_top].val<a[i]){
			dow_l=dow[dow_top].l;dow_top--;
		}
		dow[++dow_top].val=a[i];dow[dow_top].l=dow_l;
		change_max(1,dow_l,i,a[i]);
		pair<long long, long long> now=ask(1,i);
	//	cout<<now.first<<" "<<now.second<<endl;
		if(now.first==i)ans+=now.second;
	}
	/*change_max(1,1,1,a[1]);
	change_min(1,1,1,a[1]);
	change_max(1,1,2,a[2]);
	change_min(1,2,2,a[2]);
	pair<int, int> now;
	now=ask(1,2);
	cout<<now.second<<endl;*/
	printf("%lld\n",ans);
	return 0;
}
/*
5
1 2 3 5 4
*/	
	
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值