【研究ノート】キャットドライバセグメントツリー

導入

  • 古典的な問題:取ら支持部の配列を考える\(\分\) 指定された\(L、R、X \ ) 全て満たす\(L \ルI \ル R \) A \(a_iを\)修飾(\分(a_iを、X)\)\)と加算間隔

  • 必要です\(O((N + Q )nはログ\)\) 時間内に決着

アルゴリズムのプロセス

  • JIドライバセグメントツリーは、ツリーの行電位は断面を実現することができるされている\(\分/ \最大\ ) セグメント合計

  • \(\分\)一例として、各ノードは線分ツリーを4つの値を維持します。

  • (1)\(MX \) 最大範囲

  • (2)\(CNT \) 発生回数が最大範囲

  • (3)\(MD \) 厳密に小さい最大値と最大値よりも数)の時間間隔が大きいです。

  • (4)\(SUM \) 間隔と

  • 取る達成間隔\(\分\) インタロゲーション・インターバル内のノードからなる線分再帰ツリー\(P \) 処理を次の

  • (1)の場合、\(X- \ GEのmx_p \) この改正は、ノードには影響しないことは明らかである\(P-\) ダイレクトリターン

  • (2)の場合、\(X- \ルmd_p \) そして暴力に(P \)\左と右の子が再帰的にノード

  • (3)それ以外の場合は\(md_p <X- <mx_p \)の修正(sum_pが\)\のように計算することができます\(CNT_P \タイムズ(mx_p-X-)\) あなたはマークをヒットすることができます

  • アルゴリズムの複雑さを示して考えてみましょう。

  • 間隔に注意してください\(\分\)だけ間隔の異なる番号が同じになることを

  • 簡単にツリーラインにノードを取得するには、\(P \) 最終変更操作が唯一のノードまでのようになります\(pは\)に代表される間隔内の数の異なる種類の数を増やす(1 \)\

  • 間隔は、ノードの動作を変更するために再帰的暴力である場合の数を減らすために結合した異なる種類の数を表します。

  • つまり、すべてのノードの数であり、最大で、異なる種類の数を増やす\(O(Q \ Nログ )\) 倍もアップすることによって低減しなければならない(O((N + Q \ )\ Nログ)\) 回

  • したがって、合計時間の複雑\(O((N + Q )\ Nログ)\)

プラスの範囲と組み合わせること

  • キルギスタンドライバセグメントツリーはまた、範囲の組み合わせとマーキングにひとつ以上の添加に添加することができます

  • しかし、それは注目に値するです:そこのセクションがあり、プラス操作の複雑さがあることをした場合より、\(\ログ\)

  • わずか分析

CF1290Eデカルトツリー

問題の意味

  • 与えられた\(N- \)配置の\(P \)

  • \(1 \ Iル\ N-LE \) 構成が全て取得する(\ \ルI \)デカルトツリーの配列(相対的な位置関係を維持する)(根大ヒープ)を構成する全ノードの数を木と子の大きさ

  • \(1つの\ n \ 150000 \)

練習

  • 以下のための\(N- \)配列要素\(A \)デカルトノードツリーであり、\(I \)サブツリーサイズ\(1-nxt_i-pre_i \)(\ (pre_i \)\ (nxt_i \)されている\(a_iを\)左、右の第1のよりも大きい(a_iを\)\それが存在しない場合、位置\(0 \)\(N + 1 \)

  • だから、\(I \)小さなからすべてを維持するために大規模に\(\ PRE)及び(NXT \)\とすることができ

  • しかし、メンテナンスを容易にするために、我々は、動的なシーケンスを維持するが、長さではありません\(N \)すべての小のシリアル番号に大きな活性化、および\(事前\)\(NXTは\)再定義しました:

  • もし\(I \)最初の左側にその作動位置よりも大きい\(J \) 次いで\(pre_iは\)に等しい\(J \)と右側の総数があれば、活性化され\ (J \)存在しない\(pre_i \)すべてのアクティブの数に加算される\(1 \)

  • 場合\(I \)がその作動位置の第1の右よりも大きい(J \)\、次に\(nxt_i \)はに等しい(J \)\左側の総数が活性化され、場合\ (J \)は、存在しない(nxt_i \)\すべてのアクティブの数に加算される(1 \)\

  • 前者は参加している場合、これは計算することができます(私は\)\答えの数が少ないです(\ sum_j(pre_j + nxt_j \ \ 2-2i ^)-i)

  • 最初の場合\(Iは\)第一の配列を見\(X \)桁が活性化される(すなわち\(p_x = I \) 場合:

  • \(pre_x = nxt_x = I + 1 \) 直接処理することができます

  • 場合(Y <X \)\、次に\(nxt_y \)の値がなければならない\(C \)を取る(分\ \)\(\ (C \)位置のための\(Xは\)活性化された番号を残し含む数、\(X \)そのもの)

  • 場合\(Yは、> X \) 次いで\(nxt_y \)を加えなければならない(\ 1)\

  • すなわち取る間隔を達成するために、ある\(\分\) することができ、プラス範囲、キルギスタンドライバセグメントツリーメンテナンス(\ PRE)\同様

  • \(O(N \ログ^ 2N)\)

コード

#include <bits/stdc++.h>
#define p2 p << 1
#define p3 p << 1 | 1

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}

template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}

typedef long long ll;

const int N = 15e4 + 5, M = 6e5 + 5, INF = 0x3f3f3f3f;

int n, p[N], A[N];

void change(int x, int v)
{
	for (; x <= n; x += x & -x)
		A[x] += v;
}

int ask(int x)
{
	int res = 0;
	for (; x; x -= x & -x) res += A[x];
	return res;
}

struct elem
{
	int mx, cnt, md;
	
	friend inline elem operator + (elem a, elem b)
	{
		elem res;
		if (a.mx > b.mx) res = a, res.md = Max(a.md, b.mx);
		else if (a.mx < b.mx) res = b, res.md = Max(a.mx, b.md);
		else res = a, res.cnt = a.cnt + b.cnt, res.md = Max(a.md, b.md);
		return res;
	}
};

struct seg
{
	elem T[M]; int add[M], tag[M], cnt[M]; ll sum[M];
	
	void build(int l, int r, int p)
	{
		T[p] = (elem) {-INF, 0, -INF}; add[p] = 0; tag[p] = INF;
		if (l == r) return;
		int mid = l + r >> 1;
		build(l, mid, p2); build(mid + 1, r, p3);
	}
	
	void down(int p)
	{
		T[p2].mx += add[p]; T[p2].md += add[p]; sum[p2] += 1ll * cnt[p2] * add[p];
		add[p2] += add[p]; tag[p2] += add[p];
		T[p3].mx += add[p]; T[p3].md += add[p]; sum[p3] += 1ll * cnt[p3] * add[p];
		add[p3] += add[p]; tag[p3] += add[p];
		if (tag[p] < T[p2].mx) sum[p2] -= 1ll * T[p2].cnt * (T[p2].mx - tag[p]),
			tag[p2] = T[p2].mx = tag[p];
		if (tag[p] < T[p3].mx) sum[p3] -= 1ll * T[p3].cnt * (T[p3].mx - tag[p]),
			tag[p3] = T[p3].mx = tag[p];
		add[p] = 0; tag[p] = INF;
	}
	
	void upt(int p)
	{
		cnt[p] = cnt[p2] + cnt[p3]; sum[p] = sum[p2] + sum[p3];
		T[p] = T[p2] + T[p3];
	}
	
	void unlock(int l, int r, int pos, int v, int p)
	{
		if (l == r) return (void) (cnt[p] = T[p].cnt = 1, T[p].mx = sum[p] = v);
		int mid = l + r >> 1; down(p);
		if (pos <= mid) unlock(l, mid, pos, v, p2);
		else unlock(mid + 1, r, pos, v, p3);
		upt(p);
	}
	
	void change(int l, int r, int s, int e, int v, int p)
	{
		if (e < l || s > r) return;
		if (s <= l && r <= e) return (void) (add[p] += v, tag[p] += v,
			T[p].mx += v, T[p].md += v, sum[p] += 1ll * cnt[p] * v);
		int mid = l + r >> 1; down(p);
		change(l, mid, s, e, v, p2); change(mid + 1, r, s, e, v, p3);
		upt(p);
	}
	
	void modify(int l, int r, int s, int e, int v, int p)
	{
		if (e < l || s > r) return;
		if (s <= l && r <= e && v > T[p].md)
		{
			if (v < T[p].mx) sum[p] -= 1ll * T[p].cnt * (T[p].mx - v),
				tag[p] = T[p].mx = v;
			return;
		}
		int mid = l + r >> 1; down(p);
		modify(l, mid, s, e, v, p2); modify(mid + 1, r, s, e, v, p3);
		upt(p);
	}
} T1, T2;

int main()
{
	int x;
	read(n);
	for (int i = 1; i <= n; i++) read(x), p[x] = i;
	T1.build(1, n, 1); T2.build(1, n, 1);
	for (int i = 1; i <= n; i++)
	{
		change(p[i], 1); int c = ask(p[i]);
		T1.modify(1, n, 1, p[i], c, 1); T1.change(1, n, p[i], n, 1, 1);
		T2.modify(1, n, p[i], n, i - c + 1, 1); T2.change(1, n, 1, p[i], 1, 1);
		T1.unlock(1, n, p[i], i + 1, 1); T2.unlock(1, n, p[i], i + 1, 1);
		printf("%lld\n", T1.sum[1] + T2.sum[1] - 1ll * i * (i + 2));
	}
	return 0;
}

セクションK小さな値

問題の意味

  • 長さ\(N- \)配列、すべての数である\([1、N] \ ) での正の整数

  • \(N- \)の操作を、各操作が断面である\(\分\)または間隔シーク(\ K)を\小さな値

  • \(1 \ nは、M \ 8 \ times10 ^ 4 \)

練習

  • 簡単には、線分ツリーツリーカバー重量の位置を考えること

  • クエリは、呼び掛けゾーンに分割される場合\(O(\ Nログ) \) 外層は、ノードを行う、これらのノードは、セグメントに二分木を埋め込み

  • 修飾のために、ツリーラインの外側の層が取るためにマークされている\(\分\)マークが、ここで問題がある。ためには、ノードによってフラグが付けられ、その祖先ノード埋め込みセグメントツリーを迅速に修正さ達成することができません。

  • だから、ツリーラインを考慮し、キルギスドライバは同じことを考えた:修正の同じ番号が一緒に入れ。

  • 外側セグメントツリーの分割すべての変更場合\(O(\ログN) \) ノードは、ノードがすべて見つける異なる性質がより大きい\(X \)ノードの数とその中のそれらの先祖でありますこれらの番号に変更\(X \)

  • 異なる性質の保存のノードのこの数内の各音符の数\(1 \) であることが\(O(\ Nログ) \) 木が必要とされる内側セグメントを、変更行い、木O((\ \ログ^ 2N)\)複雑

  • したがって、合計複雑\(O((N + Q )\ログ^ 3N)\)

コード

#include <bits/stdc++.h>
#define p2 p << 1
#define p3 p << 1 | 1

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}

const int N = 8e4 + 5, M = N << 2, L = 3e7 + 5;

int n, m, a[N], rt[M], tag[M], ToT, tot, pt[N], nc, del[L], qaq, pos[N], val[N];

struct seg
{
	int lc, rc, sum;
} T[L];

inline int newnode() {return nc ? del[nc--] : ++ToT;}

inline void delnode(int p) {T[p].lc = T[p].rc = T[p].sum = 0; del[++nc] = p;}

void change(int l, int r, int pos, int v, int &p)
{
	if (!v) return;
	if (!p) p = newnode(); T[p].sum += v;
	if (l == r) return;
	int mid = l + r >> 1;
	if (pos <= mid) change(l, mid, pos, v, T[p].lc);
	else change(mid + 1, r, pos, v, T[p].rc);
}

void dfs(int p)
{
	if (!p) return;
	dfs(T[p].lc); dfs(T[p].rc);
	delnode(p);
}

int ask(int l, int r, int x, int p)
{
	if (l == r) return 0;
	int mid = l + r >> 1, res;
	if (x <= mid) res = ask(l, mid, x, T[p].lc) + T[T[p].rc].sum,
		dfs(T[p].rc), T[p].rc = 0;
	else res = ask(mid + 1, r, x, T[p].rc);
	return T[p].sum = T[T[p].lc].sum + T[T[p].rc].sum, res;
}

void zzq(int x, int &p) {change(1, n, x, ask(1, n, x, p), p);}

void build(int l, int r, int p)
{
	tag[p] = n;
	for (int i = l; i <= r; i++) change(1, n, a[i], 1, rt[p]);
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, p2); build(mid + 1, r, p3);
}

void down(int p)
{
	tag[p2] = Min(tag[p2], tag[p]);
	tag[p3] = Min(tag[p3], tag[p]);
	zzq(tag[p], rt[p2]); zzq(tag[p], rt[p3]);
	tag[p] = n;
}

void zhouzhouzka(int l, int r, int x, int p)
{
	if (x > r || !T[p].sum) return;
	if (l == r) return (void) (pos[++qaq] = l, val[qaq] = T[p].sum);
	int mid = l + r >> 1;
	zhouzhouzka(l, mid, x, T[p].lc);
	zhouzhouzka(mid + 1, r, x, T[p].rc);
}

void getmin(int l, int r, int s, int e, int x, int p)
{
	if (e < l || s > r) return;
	if (s <= l && r <= e)
	{
		tag[p] = Min(tag[p], x); qaq = 0; zhouzhouzka(1, n, x + 1, rt[p]);
		zzq(x, rt[p]);
		for (int i = 1; i <= qaq; i++)
			for (int q = p >> 1; q; q >>= 1)
				change(1, n, pos[i], -val[i], rt[q]),
					change(1, n, x, val[i], rt[q]);
		return;
	}
	int mid = l + r >> 1; down(p);
	getmin(l, mid, s, e, x, p2); getmin(mid + 1, r, s, e, x, p3);
}

void czx(int l, int r, int s, int e, int x, int p)
{
	if (e < l || s > r) return;
	if (s <= l && r <= e) return (void) (pt[++tot] = rt[p]);
	int mid = l + r >> 1; down(p);
	czx(l, mid, s, e, x, p2); czx(mid + 1, r, s, e, x, p3);
}

int query(int l, int r, int k)
{
	tot = 0; czx(1, n, l, r, n, 1);
	int d = 0;
	for (int i = 1; i <= tot; i++) d += T[pt[i]].sum;
	l = 1; r = n;
	while (l < r)
	{
		int delta = 0, mid = l + r >> 1;
		for (int i = 1; i <= tot; i++) delta += T[T[pt[i]].lc].sum;
		if (k <= delta)
		{
			r = mid;
			for (int i = 1; i <= tot; i++) pt[i] = T[pt[i]].lc;
		}
		else
		{
			k -= delta; l = mid + 1;
			for (int i = 1; i <= tot; i++) pt[i] = T[pt[i]].rc;
		}
	}
	return l;
}

int main()
{
	int op, l, r, x;
	read(n); read(m);
	for (int i = 1; i <= n; i++) read(a[i]);
	build(1, n, 1);
	while (m--)
	{
		read(op); read(l); read(r); read(x);
		if (op == 1 && x > n) x = n;
		if (op == 1) getmin(1, n, l, r, x, 1);
		else printf("%d\n", query(l, r, x));
	}
	return 0;
}

おすすめ

転載: www.cnblogs.com/xyz32768/p/12590112.html