CF526F:Pudding Monsters 题解
F:
题意:输入排列p,求有多少长度为 L 的区间满足 \(max-min+1=L\) . (n<=1e5)
Solution:
一道比较经典的数据结构题。
由于是排列,因此需要想到:枚举数字和枚举位置的思路其实是一样的。事实上原题面是二维平面每行每列都有一个点,求多少边长为 L 的方框内有 L 个点。
那么我们枚举右端点 r,求有多少个左端点 l 满足题意。
我们用线段树维护 \(max[l,r]-min[l,r]-L[l,r]+1\),这是个比较有启发的做法,虽然维护的内容比较奇特,但是易于区间更新。
马上我们就会发现 max,min,L 都是可以更新的。
更新 L 最简单,每次 r 向右枚举一位,所有的区间长度加一,即线段树区间加。
更新 max,min 原理相同,这两个值在固定右端点时向左都是单调的,可以用单调栈来维护,每次弹出一个数或压入一个数都可以用线段树区间加的形式来更新。为了避免同一数字重复出入栈,我们记录每个数字的数值以及出现了几次(同样方法参考CF1905D题解)。
更新是区间加,那么我们具体维护的是什么呢,维护的是最小值以及最小值出现次数。因为你会发现所有合法的情况满足 \(max-min-L+1=0\) ,而且不可能有负数出现,所以每次询问线段树最小值,如果是0答案加上出现次数(事实上由于l=r的情况存在,最小值肯定是0)。
数据结构的代码是基本功,没有特殊点。
ll T;
ll n;
ll a[N];
struct E{
ll l,r,zhi;
}da[N],xi[N];
ll cnt1,cnt2;
struct D{
ll mi,num;
}t[N<<2],ling;
ll tag[N<<2];
inline D pushup(D A,D B) {
D C = ling;
if(A.mi==B.mi) C.num = A.num + B.num;
else if(A.mi<B.mi) C.num = A.num;
else if(A.mi>B.mi) C.num = B.num;
C.mi = min(A.mi, B.mi);
return C;
}
inline void pushdown(ll now) {
t[ls].mi += tag[now]; t[rs].mi += tag[now];
tag[ls] += tag[now]; tag[rs] += tag[now];
tag[now] = 0;
}
void built(ll now,ll l,ll r) {
if(l==r) { t[now].mi = 0; t[now].num = 1; return ; }
ll mid = l+r >> 1;
built(ls, l, mid);
built(rs, mid+1, r);
t[now] = pushup(t[ls], t[rs]);
}
void insert(ll now,ll l,ll r,ll x,ll y,ll g) {
if(x<=l && r<=y) { t[now].mi += g; tag[now] += g; return ; }
pushdown(now);
ll mid = l+r >> 1;
if(x<=mid) insert(ls, l, mid, x, y, g);
if(y>mid) insert(rs, mid+1, r, x, y, g);
t[now] = pushup(t[ls], t[rs]);
}
D query(ll now,ll l,ll r,ll x,ll y) {
if(x<=l && r<=y) return t[now];
pushdown(now);
ll mid = l+r >> 1;
D A = ling, B = ling;
if(x<=mid) A = query(ls, l, mid, x, y);
if(y>mid) B = query(rs, mid+1, r, x, y);
return pushup(A,B);
}
int main() {
n = read();
da[0] = {0,0,inf};
xi[0] = {0,0,-inf};
built(1,1,n);
for(ll i=1;i<=n;i++) a[read()] = read();
ll lst = 0, ans = 0;
for(ll i=1;i<=n;i++) {
while(cnt1 && da[cnt1].zhi<a[i]) insert(1, 1, n, da[cnt1].l, da[cnt1].r, -da[cnt1].zhi), cnt1--;
lst = da[cnt1].r;
insert(1, 1, n, lst+1, i, a[i]); da[++cnt1] = {lst+1,i,a[i]};
while(cnt2 && xi[cnt2].zhi>a[i]) insert(1, 1, n, xi[cnt2].l, xi[cnt2].r, xi[cnt2].zhi), cnt2--;
lst = xi[cnt2].r;
insert(1, 1, n, lst+1, i, -a[i]); xi[++cnt2] = {lst+1,i,a[i]};
insert(1, 1, n, 1, i, -1);
ans += query(1, 1, n, 1, i).num;
}
cout<<ans;
return 0;
}