P8600 [蓝桥杯 2013 省 B] 连号区间数 题解

析合树是一种连续段数据结构。

引入:

给定排列 {Pn},求值域连续的段的个数。

概念

排列 是排列。

连续段 是值域连续的段,满足集合运算。

值域区间 是连续段值域的区间。

本原段 是不与其他连续段部分相交的连续段。

连续段集 Ip本原段集 Mp

形式化的,

定义序列 Pn排列 |P|=ni[1,n],iP

定义 (P,[l,r])连续段 x,z[l,r],y[l,r],Px<Py<Pz

连续段 (P,[l,r]) 满足 [l,r] 上的集合运算。

定义 (P,[l,r])值域区间[mini=lrPi,maxi=lrPi]

定义连续段集 Ip={A|A 是连续段}

定义连续段 X本原段 AIp,XA=(P,)XAAX

定义本原段集 Mp={A|A 是本原段}

析点与合点

析合树的点集是 Mp

概念

节点 u值域区间[ul,ur]

节点 u子节点序列 Suu极长真子连续段序列。

节点 u子节点排列 PuSu 按值域区间左端点离散化的结果。

节点 u合点 当且仅当 Pu 有序。

节点 u析点 当且仅当 u 不是合点。

形式化的,

定义节点 u值域区间[ul,ur]

定义节点 u子节点序列 Su={v|vu,wu,vw},且按 v左端点排序。

注意,v左端点不是 v值域区间左端点

定义节点 u子节点排列 Pu,i=|{vlSu,il|vSu}|

定义节点 u合点 i[1,|Su|],Pu,i=ii[1,|Su|],Pu,i=|Su|i+1

定义节点 u析点 u 不是合点。

性质

u 的子节点的并是 u。显然。

u 是合点当且仅当 Su 的子段构成连续段。显然。

u 是析点当且仅当 Su 的多元素子段不构成连续段。

证明:若命题不成立,则 Su 上构成连续段的极长子段构成本原段,而此本原段未在析合树中出现。

形式化的,

vSuv=u

u 是合点 [l,r][1,|Su|],i=lrSu,iIp

u 是析点 [l,r][1,|Su|],l<r,i=lrSu,iIp

建树

可以用 增量法 O(nlogn) 建析合树。

Pi 依次加入析合树,用一个栈维护 Pj|j[1,i) 形成的析合森林。

策略

维护当前节点 u(初值为 (P,[i,i])),考虑 u 与栈内节点的合并情况。

  1. u 能成为栈顶节点的子节点 栈顶节点是合点且 u 与栈顶节点的最右子节点能合并成连续段,令 u 成为栈顶节点的子节点,然后令 u 栈顶节点。
  2. u 不能成为栈顶节点的子节点,令 u 与栈顶若干节点合并成连续段 v|Sv| 最小。考虑 v 的类型,容易发现此时 v 是合点 |Sv|=2。令 uv
  3. 重复上述方案直到不能进行(即找不到 2. 中合法的 v),将 u 入栈。

(来自 CF,对 {9,1,10,3,2,5,7,6,8,4} 建析合树)

根据上述策略,我们需要快速判断 u 与一些点能否合并成连续段,即判断任意后缀 [j,i]|j[1,i) 是否连续段。

子问题

考虑如何快速判断 [j,i]|j[1,i) 是否连续段。

P 是排列,所以 [j,i] 是连续段当且仅当 maxjkiminjki=ij

维护 Qj=maxjkiminjkii+j|j[1,i),则 [j,i] 是连续段当且仅当 Qj=0

考虑更新 i 时如何快速更新 Q

maxjkiminjkii+j 中,j 是不变的,每次更新 iQj 的贡献减一,最值的贡献不好维护。

Pi 更新了 Bi 个后缀最值的位置,注意到 i=1nBi=O(n)(单调栈结论)。

用单调栈维护后缀最值的位置,以后缀最小值为例。

维护单调递增的栈 x。不难发现 Pxi 是后缀 [j,i]|j(xi1,xi] 的最小值。

xi 出栈时失去对 Qj|j(xi1,xi] 的贡献,所以 Qj|j(xi1,xi]Qj+Pxi

i 入栈时获得对 Qj|j(xtop,i] 的贡献,所以 Qj|j(xtop,i]QjPi

后缀最大值同理。

关于本题

考虑析合树内每个点的贡献。

由合点的性质,若 u 是合点,则 Su 的任意子段构成连续段,即 u 的贡献为 (|Su|2)

由析点的性质,若 u 是析点,则 Su 的任意多元素子段不构成连续段,即 u 的贡献为 1

注意到 |Mp|=O(n),所以时间复杂度 O(nlogn)

#include <cstdio>
#include <vector>
#define A(u, v) u->c.push_back(v)
using namespace std;
int n, L, X, Y, a[50050], x[50050], y[50050];
long long q;
struct S
{
    int l, r;
    bool b;
    vector<S *> c;
} * u, *f, *s[50050];
struct T
{
    T *l, *r;
    int s, t, v, x;
    T(int p, int q) : s(p), t(q), v(0), x(0) {}
    void f(int p)
    {
        v += p;
        x += p;
    }
    void u() { v = min(l->v, r->v); }
    void d()
    {
        l->f(x);
        r->f(x);
        x = 0;
    }
} * r;
void D(S *p)
{
    q += p->b ? 1ll * p->c.size() * (p->c.size() - 1) >> 1 : 1;
    for (auto c : p->c)
        D(c);
}
void B(int s, int t, T *&p)
{
    p = new T(s, t);
    if (s != t)
    {
        int m = p->s + p->t >> 1;
        B(s, m, p->l);
        B(m + 1, t, p->r);
        p->u();
    }
}
void M(int l, int r, int x, T *p)
{
    if (l <= p->s && p->t <= r)
        return p->f(x);
    p->d();
    int m = p->s + p->t >> 1;
    if (l <= m)
        M(l, r, x, p->l);
    if (r > m)
        M(l, r, x, p->r);
    p->u();
}
bool Q(int l, T *p)
{
    if (p->s == p->t)
        return !p->v;
    p->d();
    int m = p->s + p->t >> 1;
    return l <= m ? Q(l, p->l) : Q(l, p->r);
}
int Z(T *p)
{
    if (p->s == p->t)
        return p->s;
    p->d();
    int m = p->s + p->t >> 1;
    return p->l->v ? Z(p->r) : Z(p->l);
}
int main()
{
    scanf("%d", &n);
    B(1, n, r);
    for (int i = 1; i <= n; ++i)
        scanf("%d", a + i);
    for (int i = 1, p, o; i <= n; ++i)
    {
        while (X && a[i] <= a[x[X]])
            M(x[X - 1] + 1, x[X], a[x[X]], r), --X;
        while (Y && a[i] >= a[y[Y]])
            M(y[Y - 1] + 1, y[Y], -a[y[Y]], r), --Y;
        M(x[X] + 1, i, -a[i], r);
        M(y[Y] + 1, i, a[i], r);
        x[++X] = y[++Y] = i;
        u = new S{i, i, 0};
        p = Z(r);
        while (L && s[L]->l >= p)
            if (s[L]->b && Q(s[L]->c.back()->l, r))
                s[L]->r = i, A(s[L], u), u = s[L--];
            else
            {
                f = new S{0, i, Q(s[o = L]->l, r)};
                while (!Q(s[L]->l, r))
                    --L;
                for (int j = L; j <= o; ++j)
                    A(f, s[j]);
                f->l = s[L--]->l;
                A(f, u);
                u = f;
            }
        s[++L] = u;
        M(1, i, -1, r);
    }
    return D(s[1]), printf("%lld", q), 0;
}
posted @   Jijidawang  阅读(29)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示