renren-fast部署釋出教程(tomcat)
可持久化線段樹(主席樹)
單點修改
1.單點修改時,我們考慮將包含該點\(k\)的線段樹節點新建出一條鏈。(就像這樣) 每次修改將創造出\(logn\)個新節點。
2.修改完的線段樹不再是一顆完全二叉樹,我們不能直接用層次編號,而是直接改為記錄左右子節點的編號。大概的意思就是:不能用\(o << 1\)的方式去找o點的左兒子,而是要在結構體裡新加一個東西,用\(t[o].lc\)去找他的左兒子。
struct Tree { int lc, rc; //左右子樹編號 int dat; //區間最大值 } t[N << 2]; int tot, root[N]; //可持久化線段樹的總點數和每個根 int n, a[N]; void up(int p) { t[p].dat = max(t[t[p].lc].dat, t[t[p].rc].dat); } int build(int l, int r) { int p = tot++; if(l == r) { t[p].dat = a[l]; return p; } int mid = (l + r) >> 1; t[p].lc = build(l, mid); t[p].rc = build(mid + 1, r); up(p); return p; } root[0] = build(1, n); //呼叫入口 int insert(int now, int l, int r, int x, int val) { int p = tot++; t[p] = t[now]; if(l == r) { t[p].dat = val; return ; } int mid = (l + r) >> 1; if(x <= mid) t[p].lc = insert(t[now].lc, l, mid, x, val); if(x > mid) t[p].rc = insert(t[now].rc, mid + 1, r, x, val); up(p); return p; } root[i] = insert(root[i - 1], 1, n, x, val); //呼叫入口 cout << ask(root[v], 1, n, x);
然後這個題就可以做了:
區間第k小值
思想個上面那個差不多,只是用主席樹的方法做。
P3834 【模板】可持久化線段樹 2(主席樹)(和poj2761一樣,但是上面那個程式碼在poj過了,在luogu就過不了,不知道為啥,就很ex)
通過模擬資料來發現一些規律:
7
1 5 2 6 3 7 4
2 5 3
(第一棵樹)
(第二棵樹)
像這樣一直插完。。。
(第七棵樹)
現在我們要詢問區間[2, 5]第3大值,我們把第一棵樹和第五棵樹拿出來。
我們可以發現,對應節點的數字相減就可以得到1區間[2, 5]的數。(有點類似於字首和)
我們設\(u\)為編號小的樹, \(v\)為編號大的樹,設\(x = t[t[v.ls]].sum - t[t[u.ls]].sum\)
這個主席樹上是值域。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define mid ((l + r) >> 1)
using namespace std;
inline int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
return s * f;
}
const int N = 2e5 + 5;
int n, m, tot;
int a[N], b[N], root[N * 20];
struct tree { int lc, rc, sum; } t[N * 20];
int build(int l, int r) {
int p = ++tot;
if(l == r) { return p; }
t[p].lc = build(l, mid); t[p].rc = build(mid + 1, r);
return p; //一定要記得寫,不然會RE
}
int change(int now, int l, int r, int k) {
int p = ++tot;
t[p].lc = t[now].lc; t[p].rc = t[now].rc; t[p].sum = t[now].sum + 1;
if(l == r) { return p; }
if(k <= mid) t[p].lc = change(t[now].lc, l, mid, k);
if(k > mid) t[p].rc = change(t[now].rc, mid + 1, r, k);
return p; //一定記得寫
}
int query(int u, int v, int l, int r, int k) {
if(l == r) return l;
int x = t[t[v].lc].sum - t[t[u].lc].sum;
if(k <= x) return query(t[u].lc, t[v].lc, l, mid, k);
if(k > x) return query(t[u].rc, t[v].rc, mid + 1, r, k - x);
}
int main() {
n = read(); m = read();
for(int i = 1;i <= n; i++) b[i] = a[i] = read();
sort(b + 1, b + n + 1);
int cnt = unique(b + 1, b + n + 1) - b - 1;
root[0] = build(1, cnt); //這是那個空樹,注意那個cnt,離散化之後序列長度就變為cnt了(把重複的數去掉了)
for(int i = 1;i <= n; i++) {
a[i] = lower_bound(b + 1, b + cnt + 1, a[i]) - b;
root[i] = change(root[i - 1], 1, cnt, a[i]);
}
for(int i = 1;i <= m; i++) {
int x = read(), y = read(), k = read();
printf("%d\n", b[query(root[x - 1], root[y], 1, cnt, k)]);
}
return 0;
}
水題
題目大意:給一個數列,每次詢問一個區間內有沒有一個數出現次數超過一半。沒有的話輸出0。
主席樹水題。。。
我們現在要找一個區間[\(l\), \(r\)]的一個數出現次數大於\((r - l + 1)/2\)。令\(x = (r - l + 1)/2\), 我們像上面區間第\(k\)小值一樣維護一個\(sum\)。如果一個節點的左兒子的\(sum * 2\)小於等於\(x\),那麼這裡面肯定沒有符合要求的數。如果大於,這個數則在左子樹內;右子樹同理。
#include <iostream>
#include <cstdio>
#include <cctype>
#define mid ((l + r) >> 1)
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 7e5 + 5;
int n, m, tot;
int root[N];
struct tree { int lc, rc, sum; } t[N * 20];
int build(int l, int r) {
int p = ++tot;
if(l == r) { return p; }
t[p].lc = build(l, mid); t[p].rc = build(mid + 1, r);
return p;
}
int insert(int res, int l, int r, int x) {
int p = ++tot;
t[p].lc = t[res].lc; t[p].rc = t[res].rc; t[p].sum = t[res].sum + 1;
if(l == r) { return p; }
if(x <= mid) t[p].lc = insert(t[res].lc, l, mid, x);
if(x > mid) t[p].rc = insert(t[res].rc, mid + 1, r, x);
return p;
}
int query(int L, int R, int l, int r, int x) {
if(l == r) { return l; }
int num1 = t[t[R].lc].sum - t[t[L].lc].sum;
int num2 = t[t[R].rc].sum - t[t[L].rc].sum;
if(num1 * 2 > x) return query(t[L].lc, t[R].lc, l, mid, x);
else if(num2 * 2 > x) return query(t[L].rc, t[R].rc, mid + 1, r, x);
else return 0;
}
int main() {
n = read(); m = read();
root[0] = build(1, n);
for(int i = 1, x;i <= n; i++) {
x = read(); root[i] = insert(root[i - 1], 1, n, x);
}
for(int i = 1, l, r;i <= m; i++) {
l = read(); r = read();
printf("%d\n", query(root[l - 1], root[r], 1, n, r - l + 1));
}
return 0;
}