Pudding Monster
题目连接:https://www.luogu.org/problem/show?pid=CF526F
问题提出
- 给长度为nnn的排列AAA.
- 问有多少(l,r)(l,r)(l,r),使得将Al,Al+1,...,ArA_l,A_{l+1},...,A_rAl,Al+1,...,Ar排序之后是连续的一段数.
- n≤105n \le 10^5n≤105
问题解决
判断一段区间是否能连续,只要判断是否满足max−min=r−lmax-min=r-lmax−min=r−l即可.
也即判断区间是否满足max−min+l=rmax-min+l = rmax−min+l=r.
先天有max−min+l≥rmax-min+l \ge rmax−min+l≥r
因此我们可以考虑枚举区间端点rrr,然后使用线段树维护左边所有区间(每个区间用左端点唯一标识)的l+max−minl+max-minl+max−min的值.
我们还需要线段树满足求线段内最小值是谁,最小值有多少个的功能.
考虑到区间右端点向右移动的过程中,原有的区间再加上rrr形成的新区间的maxmaxmax不会变小,minminmin值不会变大.
而maxmaxmax变大也会是一批区间一起增大,增量也是相同的,因此可以转化为区间修改操作.
我们可以用222个单调栈来维护当前的最大值序列,和最小值序列.
每次将rrr点加入左边所有的区间,从而形成新的区间,根据单调栈保存的信息,确定出哪些区间的最大值或者是最小值需要被修改,然后进行区间修改.
询问时候,直接查询[1,r][1,r][1,r]内最小值为000的个数,算贡献.
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
#define clr(x) memset(x,0,sizeof(x))
#define setinf(x) memset(x,0x3f,sizeof(x))const int N = 300007,inf = 1e9;
int n,m;
typedef std::pair<int,int> pii;
int a[N];struct node{int val,cnt,tag,len;node(int val = 0,int cnt = 1,int tag = 0,int len = 1):val(val),cnt(cnt),tag(tag),len(len){}
}ns[N<<2];
//线段树维护的是区间最小值以及最小值的个数
inline void pushdown(int o) {if(ns[o].tag) {ns[o<<1].val += ns[o].tag;ns[o<<1|1].val += ns[o].tag;ns[o<<1].tag += ns[o].tag;ns[o<<1|1].tag += ns[o].tag;ns[o].tag = 0;}
}inline node maintain(node &lch,node &rch) {node res = node(std::min(lch.val,rch.val),0,0,lch.len+rch.len);if(lch.val == res.val) res.cnt += lch.cnt;if(rch.val == res.val) res.cnt += rch.cnt;return res;
}inline void change(int o,int l,int r,int cl,int cr,int val) {if(cl <= l && r <= cr) {ns[o].tag += val;ns[o].val += val;return ;} if(r < cl || cr < l) return;int mid = (l + r) >> 1;pushdown(o);change(o<<1,l,mid,cl,cr,val);change(o<<1|1,mid+1,r,cl,cr,val);ns[o] = maintain(ns[o<<1],ns[o<<1|1]);
}inline node query(int o,int l,int r,int ql,int qr) {if(ql <= l && r <= qr) return ns[o];if(r < ql || qr < l)return node(inf,1,0,0);int mid = (l + r) >> 1;pushdown(o);node lch = query(o<<1,l,mid,ql,qr);node rch = query(o<<1|1,mid+1,r,ql,qr);return maintain(lch,rch);
}inline void build(int o,int l,int r) {if(l == r) {ns[o] = node(0,1,0,1);return ;}int mid = (l + r) >> 1;build(o<<1,l,mid);build(o<<1|1,mid+1,r);ns[o] = maintain(ns[o<<1],ns[o<<1|1]);
}pii mx[N],mi[N];
int topx,topi;int main() {std::cin >> n;build(1,1,n);rep(i,1,n) {int idx,y;std::cin >> idx >> y;a[idx] = y;}long long ans = 0;rep(i,1,n) {change(1,1,n,i,i,i);while(topx > 0 && mx[topx].first < a[i]) {change(1,1,n,mx[topx-1].second+1,mx[topx].second,a[i] - mx[topx].first);topx--;}mx[++topx] = (pii){a[i],i};while(topi > 0 && mi[topi].first > a[i]) {change(1,1,n,mi[topi-1].second+1,mi[topi].second,mi[topi].first - a[i]);topi--;}mi[++topi] = (pii){a[i],i};change(1,1,n,1,i,-i);node res = query(1,1,n,1,i);ans += res.cnt;change(1,1,n,1,i,i);}std::cout << ans << std::endl;return 0;
}