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;
}
posted @ 2024-02-27 17:52  maple276  阅读(32)  评论(0编辑  收藏  举报