析合树……怎么说呢,应该是一个排列的一种划分方法,用于处理连续段有关问题。
首先定义连续段:
对于一个排列的一个区间 [ x , y ] [x,y] [x,y],如果把这个区间的数拿出来排序,是连续的若干个数,即 y − x + 1 = m a x ( x . . y ) − m i n ( x . . y ) + 1 y-x+1=max(x..y)-min(x..y)+1 y−x+1=max(x..y)−min(x..y)+1,则称 [ x , y ] [x,y] [x,y]为一个连续段。
总感觉直接说划分方法有点说不清,再说一些定义:
析合树上的每个点代表的是一个区间,当然它是一棵树。
每个点的子节点的区间互不相交,且并起来是这个点代表的区间。
点分为析点和合点两种:
1.析点:
把这个的子节点重新排列,取任意长度>1的区间,均不是连续段。
2.合点:
……,均是连续段
解释重新排列:
如果一个值域是[1,10]的点的子节点的值域分别为[3,6]、[1,2]、[8,10]、[7,7]
那么重新排列就变成了2,1,4,3
一个重要的结论是:
一个排列一定可以用析合树划分出来,即root代表的就是整个排列。
划分显然不止一种,但有一种析合树划分,即极大的一种(极大不好理解没有关系),是唯一的,且有很多优美的性质。
当我们拿到一个排列时,你想直接知道root是析点还是合点都很难,所以我们要考虑自下而上,可以用增量法建树。
假设现在已经搞定了[1…i]的析和森林,我们把每个析合树的root拿出来放到一个栈里(代表区间后的在栈顶)
现在,要加入第i+1个位置,先建一个析点就表示这个位置,设为x。
设要加入的点是x,栈顶的点是y。
那么一共有三种情况:
当以上三种情况都不行时,说明不能再合并什么的了,直接把x加入栈顶。
不难得到析合树的点数是 O ( n ) O(n) O(n)的。
可以知道1、2操作的复杂度就对应树的点数,但是3操作最坏情况需要遍历整棵单调栈。
例如:当root是一个儿子个数为n的析点时,复杂度就变成了 O ( n 2 ) O(n^2) O(n2)
现在需要考虑用奇技淫巧去优化这个。
对一个区间[x…y],求出 a [ i ] ∈ [ m i n . . m a x ] a[i]∈[min..max] a[i]∈[min..max]的i的最小值和最大值,不妨设为[l…r]
显然当r>y时,右端点不变,x往左的左段点都不可能是连续段。
那么对单调栈里的每个点,维护一个fail指针,它在加入单调栈前,不是要做3操作吗?失败了才被加入单调栈,fail记录的就是倒着合并时第一次r>y的时候。
假设现在有一个新的点要做3操作,先和栈顶判一下,如果不行,就跳到栈顶的fail去,直到跳到同样的r>y时 或者 是析点了。
很容易证明 可能的析点左分界点 一定在fail链上
且这样做的复杂度是 O ( n ) O(n) O(n)的,因为一条fail链被跳过一次后就一定不会再被跳。
至于[l,r]可以处理个ST表,也可以求相邻的然后就合并,复杂度一般要一个log,用毛子算法就可以做到 O ( n ) O(n) O(n)
个人觉得这个东西并不是非常好写,但是利用模块化思想会清晰很多,大概就是用个struct来表示各种类型,再写几个函数来merge、判断是不是连续段
裸题:
JZOJ6202. c
#include
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i < B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
void cmax(int &x, int y) { x < y ? x = y : 0;}
void cmin(int &x, int y) { x > y ? x = y : 0;}
const int N = 2e5 + 5;
int n, a[N], na[N], m, x, y;
int t0[N * 4], t1[N * 4], pl, pr, px0, px1;
#define i0 i + i
#define i1 i + i + 1
void bt(int i, int x, int y) {
if(x == y) { t0[i] = t1[i] = na[x]; return;}
int m = x + y >> 1;
bt(i0, x, m); bt(i1, m + 1, y);
t0[i] = min(t0[i0], t0[i1]);
t1[i] = max(t1[i0], t1[i1]);
}
void ft(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x >= pl && y <= pr) {
cmin(px0, t0[i]);
cmax(px1, t1[i]);
return;
}
int m = x + y >> 1;
ft(i0, x, m); ft(i1, m + 1, y);
}
struct P {
int x, y;
P(){ x = 100000, y = 0;}
P(int _x, int _y) {x = _x, y = _y;}
} c[N];
void bin(P &a, P b) {
cmin(a.x, b.x);
cmax(a.y, b.y);
}
struct nod {
int ty, ls;
P a, b, c, d;
nod() {
a = b = c = d = P();
}
} b[N]; int b0;
nod bin(nod a, nod b) {
bin(a.a, b.a);
bin(a.b, b.b);
a.d = a.c; bin(a.d, b.d);
bin(a.c, b.c);
return a;
}
int len(nod a) { return a.a.y - a.a.x + 1;}
int vlen(nod a) { return a.b.y - a.b.x + 1;}
int pd(nod a) {
return len(a) == vlen(a);
}
int z[N], z0;
int f[18][N];
struct Fail {
int x;
nod a;
} d[N];
int add(int x) {
if(!z0) {
z[++ z0] = x;
return 0;
}
int y = z[z0];
if(!b[y].ty && pd(bin(b[b[y].ls], b[x]))) {
b[y] = bin(b[y], b[x]);
b[y].ls = x;
f[0][x] = y;
z0 --;
return y;
}
if(pd(bin(b[y], b[x]))) {
b[++ b0] = bin(b[y], b[x]);
b[b0].ls = x;
b[b0].ty = 0;
f[0][y] = f[0][x] = b0;
z0 --;
return b0;
}
int t = z0; y = z[t];
nod e = bin(b[y], b[x]);
while(e.d.y <= b[x].a.y && !pd(e)) {
y = z[t];
e = bin(d[t].a, e);
t = d[t].x;
}
if(pd(e)) {
b0 ++;
z[++ z0] = x;
fo(i, t, z0) {
f[0][z[i]] = b0;
b[b0] = bin(b[b0], b[z[i]]);
}
b[b0].ty = 1;
b[b0].ls = x;
z0 = t - 1; return b0;
} else {
z[++ z0] = x;
d[z0].x = t;
d[z0].a = e;
return 0;
}
}
int q[N], r[N], dep[N];
int lca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
fd(i, 17, 0) if(dep[f[i][x]] >= dep[y]) x = f[i][x];
if(x == y) return x;
fd(i, 17, 0) if(f[i][x] != f[i][y]) x = f[i][x], y = f[i][y];
return f[0][x];
}
int xia(int x, int y) {
fd(i, 17, 0) if(dep[f[i][y]] > dep[x]) y = f[i][y];
return y;
}
int ed[N];
int main() {
freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);
scanf("%d", &n);
fo(i, 1, n) scanf("%d", &a[i]), na[a[i]] = i;
bt(1, 1, n);
fo(i, 1, n - 1) {
pl = a[i], pr = a[i + 1];
if(pl > pr) swap(pl, pr);
px0 = n; px1 = 0;
ft(1, 1, n);
c[i].x = px0; c[i].y = px1;
}
fo(i, 1, n) {
b[++ b0].ty = 1;
b[b0].a = P(i, i);
b[b0].b = P(a[i], a[i]);
b[b0].c = c[i];
b[b0].d = P();
ed[i] = b0;
int x = b0;
do x = add(x); while(x);
}
fo(i, 1, b0) r[f[0][i]] ++;
fo(i, 1, b0) if(!r[i]) q[++ q[0]] = i;
for(int i = 1; i <= q[0]; i ++) {
int x = q[i];
if(f[0][x] && !(-- r[f[0][x]])) q[++ q[0]] = f[0][x];
}
fd(i, q[0], 1) dep[q[i]] = dep[f[0][q[i]]] + 1;
fo(j, 1, 17) fo(i, 1, b0) {
f[j][i] = f[j - 1][f[j - 1][i]];
}
scanf("%d", &m);
fo(i, 1, m) {
scanf("%d %d", &x, &y);
int z = lca(ed[x], ed[y]);
if(b[z].ty) {
pp("%d %d\n", b[z].a.x, b[z].a.y);
} else {
x = xia(z, ed[x]), y = xia(z, ed[y]);
nod w = bin(b[x], b[y]);
pp("%d %d\n", w.a.x, w.a.y);
}
}
}