P8600 [蓝桥杯 2013 省 B] 连号区间数 题解
析合树是一种连续段数据结构。
引入:
给定排列
,求值域连续的段的个数。
概念
排列 是排列。
连续段 是值域连续的段,满足集合运算。
值域区间 是连续段值域的区间。
本原段 是不与其他连续段部分相交的连续段。
连续段集
形式化的,
定义序列
定义
连续段
定义
定义连续段集
定义连续段
定义本原段集
析点与合点
析合树的点集是
概念
节点
节点
节点
节点
节点
形式化的,
定义节点
定义节点
注意,
定义节点
定义节点
定义节点
性质
证明:若命题不成立,则
形式化的,
建树
可以用 增量法
将
策略
维护当前节点
能成为栈顶节点的子节点 栈顶节点是合点且 与栈顶节点的最右子节点能合并成连续段,令 成为栈顶节点的子节点,然后令 栈顶节点。 不能成为栈顶节点的子节点,令 与栈顶若干节点合并成连续段 且 最小。考虑 的类型,容易发现此时 是合点 。令 。- 重复上述方案直到不能进行(即找不到 2. 中合法的
),将 入栈。
(来自 CF,对
根据上述策略,我们需要快速判断
子问题
考虑如何快速判断
维护
考虑更新
在
设
用单调栈维护后缀最值的位置,以后缀最小值为例。
维护单调递增的栈
后缀最大值同理。
关于本题
考虑析合树内每个点的贡献。
由合点的性质,若
由析点的性质,若
注意到
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具