1. 程式人生 > 實用技巧 >linux的基本命令(一)

linux的基本命令(一)

主席樹

首先考慮一個比較經典的問題,你有一個靜態的數列,每次詢問一段區間 \(l \to r\) 內的第 \(k\) 小。

做法的一句話介紹,巨的人就不要往下翻了。

對於原序列的每個字首維護一顆線段樹,維護這個區間,並且這些線段樹滿足可減性。

接下具體解釋下,

考慮一個靜態區間上維護區間資訊,一般會想到什麼呢?

還記得我們在學線段樹的時候曾經是需要維護一個可修改的區間。

我們一共三種做法。

第一種,直接用 \(a[]\) 記錄,每次修改 \(O(1)\),查詢 \(O(n)\)

第二種,通過差分的思想維護字首和陣列 \(sum[]\) 每次修改 \(O(n)\) ,查詢 \(O(1)\)

第三種,則是線段樹,查詢,修改都是 \(O(log_n)\)

的。

那麼這個問題只需要維護一段靜態區間,那麼就用差分的思想。

對於每個字首都開一顆權值線段樹,那麼這樣的話查詢就變得十分的方便。

因為只要判斷左邊的大小是否大於 \(k\) 。最裸的線段樹問題。

看似很簡單,但是空間顯然會炸。

你這麼做的空間複雜度是 \(n * n << 2\) 的。

你不炸誰炸?

那麼這個時候就可以用到動態開點了,不知道動態開點的可以去看我的 \(blog\)

這也是主席樹的靈魂部分。

記錄每個字首的根,從根開始向前一顆線段樹的子樹連邊。

具體的話是這樣實現的。

\(a[]\) 是原數列,現在處理到 \(x\) 節點。

你就依次判斷 \(mid\)\(a[x]\)

之間的關係。

如果 \(mid\) 大於 \(a[x]\) 你就把第 \(x\) 顆線段樹當前節點的右兒子連到第 \(x - 1\) 顆線段樹對應的位置。

否則就把第 \(x\) 顆線段樹當前節點的左兒子連到第 \(x - 1\) 顆線段樹對應的位置。

至於另一邊就不斷遞迴就行了,最後只需要新建一個節點就行了。

模板

程式碼

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
#define N 200020
#define ls x << 1
#define rs x << 1 | 1
#define inf 0x3f3f3f3f
#define inc(i) (++ (i))
#define dec(i) (-- (i))
#define mid ((l + r) >> 1)
#define int long long
#define XRZ 1000000003
#define debug() puts("XRZ TXDY");
#define mem(i, x) memset(i, x, sizeof(i));
#define Next(i, u) for(register int i = head[u]; i ; i = e[i].nxt)
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout);
#define Rep(i, a, b) for(register int i = (a) , i##Limit = (b) ; i <= i##Limit ; inc(i))
#define Dep(i, a, b) for(register int i = (a) , i##Limit = (b) ; i >= i##Limit ; dec(i))
int dx[10] = {1, -1, 0, 0};
int dy[10] = {0, 0, 1, -1};
using namespace std;
inline int read() {
    register int x = 0, f = 1; register char c = getchar();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x * f;
}
int n, m, T[N << 5], Data[N << 5], Left[N << 5], Right[N << 5], a[N], b[N], rt;
int Build(int l, int r) {
	int now = inc(rt);
	if(l < r) Left[now] = Build(l, mid), Right[now] = Build(mid + 1, r);
	return now;
}
int Updata(int pre, int l, int r, int x) {
	int now = inc(rt);
	Left[now] = Left[pre]; Right[now] = Right[pre]; Data[now] = Data[pre] + 1;
	if(l >= r) return now;
    if(x <= mid) Left[now] = Updata(Left[pre], l, mid, x);
	else Right[now] = Updata(Right[pre], mid + 1, r, x);
	return now;
}
int Query(int x, int y, int l, int r, int k) {
	if(l == r) return l;
	int std = Data[Left[y]] - Data[Left[x]];
	if(std >= k) return Query(Left[x], Left[y], l, mid, k);
	else return Query(Right[x], Right[y], mid + 1, r, k - std);
}
signed main() {
	int n = read(), m = read();
	Rep(i, 1, n) a[i] = b[i] = read();
	sort(b + 1, b + n + 1);
	int M = unique(b + 1, b + n + 1) - b - 1;
	T[0] = Build(1, M);
	Rep(i, 1, n) {
		a[i] = lower_bound(b + 1, b + M + 1, a[i]) - b;
		T[i] = Updata(T[i - 1], 1, M, a[i]);
	}
	Rep(i, 1, m) {
		int x = read(), y = read(), z = read();
		printf("%d\n", b[Query(T[x - 1], T[y], 1, M, z)]);
	}
	return 0;
}