析合樹學習小記

析合樹……怎麼說呢,應該是一個排列的一種劃分方法,用於處理連續段有關問題。


首先定義連續段:
對於一個排列的一個區間[x,y][x,y],如果把這個區間的數拿出來排序,是連續的若干個數,即yx+1=max(x..y)min(x..y)+1y-x+1=max(x..y)-min(x..y)+1,則稱[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。

那麼一共有三種情況:

  1. y是一個合點,x能成爲y新的兒子,拿y遞歸加入。
  2. 1不行時,新生成一個點z,作爲y和x共有合點父親,拿z遞歸加入。
  3. 1、2都不行時,新生成一個點z,作爲x和棧頂的若干點(儘量少)的共有析點父親,拿z遞歸加入。

當以上三種情況都不行時,說明不能再合併什麼的了,直接把x加入棧頂。

不難得到析合樹的點數是O(n)O(n)的。

可以知道1、2操作的複雜度就對應樹的點數,但是3操作最壞情況需要遍歷整棵單調棧。

例如:當root是一個兒子個數爲n的析點時,複雜度就變成了O(n2)O(n^2)


現在需要考慮用奇技淫巧去優化這個。

對一個區間[x…y],求出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)的,因爲一條fail鏈被跳過一次後就一定不會再被跳。

至於[l,r]可以處理個ST表,也可以求相鄰的然後就合併,複雜度一般要一個log,用毛子算法就可以做到O(n)O(n)


個人覺得這個東西並不是非常好寫,但是利用模塊化思想會清晰很多,大概就是用個struct來表示各種類型,再寫幾個函數來merge、判斷是不是連續段

裸題:
JZOJ6202. c

#include<bits/stdc++.h>
#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);
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章