BZOJ3600 沒有人的算術(替罪羊樹,線段樹)
解題思路
好神奇的數據結構題!l !1
題目大概意思是說你可以快速判斷兩個元素的大小,並且滿足偏序關係。
單點修改,求區間最大值。
題目用二元組滿足偏序關係確實巧妙。
因爲滿足偏序關係,所以我們可以給它們對應一個具體的數字,這樣比較時直接比較這個數字就行了。
但是我們需要支持動態標號,如果採用一個排列來標號的話插入一個數會有一大堆數字的變化發生變化。
我們考慮用實數來實現,整個值域從 [1,1e9] 開始,維護一個 bst,插入時自己的值就取可選值域的一半。
但事實上如果 bst 深度很大的話也會被卡滿的!!1
我們需要一個保證深度較小的平衡樹,而且不要有複雜的操作,發現替罪羊樹支持我們所有的操作,暴力重構在這題中變得十分優美。
另外序列上的問題我們需要維護一棵線段樹,線段樹要注意的是暴力重構時不必重新修改線段樹上涉及的所有節點,注意到偏序關係不會變,如果線段樹上記錄較大值是哪個節點而不是具體值即可。
代碼:
const int N = 100059;
const int M = 500909;
int V[M], cnt;
struct node {
int ls, rs;
node (int l = 0, int r = 0) : ls(l), rs(r) {}
bool operator < (const node &i) const {
return ls != i.ls ? V[ls] < V[i.ls] : V[rs] < V[i.rs];
}
bool operator == (const node &i) const { return ls == i.ls && rs == i.rs; }
}val[M];
#define ls p << 1
#define rs ls | 1
int id[N], mx[N<<2], rt, m, n;
void build(int p, int l, int r) {
mx[p] = l; if (l == r) return ;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
}
inline bool cmp(int x, int y) { return V[id[x]] >= V[id[y]]; }
void change(int p, int l, int r, int x) {
if (l == r) return mx[p] = l, void();
int mid = (l + r) >> 1;
x <= mid ? change(ls, l, mid, x) : change(rs, mid + 1, r, x);
mx[p] = cmp(mx[ls], mx[rs]) ? mx[ls] : mx[rs];
}
int query(int p, int l, int r, int L, int R) {
if (L <= l && r <= R) return mx[p];
int mid = (l + r) >> 1, res = 0;
if (L <= mid) res = query(ls, l, mid, L, R);
if (R > mid) {
int t = query(rs, mid + 1, r, L, R);
if (!cmp(res, t)) res = t;
}
return res;
}
namespace SCT {
#undef ls
#undef rs
int st[M], siz[M], ls[M], rs[M], tp;
void pia(int x) { if (ls[x]) pia(ls[x]); st[++tp] = x; if (rs[x]) pia(rs[x]); }
int build(int l, int r, int L, int R) {
int mid = (l + r) >> 1, x = st[mid];
int m2 = V[x] = (L + R) >> 1;
ls[x] = l < mid ? build(l, mid - 1, L, m2 - 1) : 0;
rs[x] = r > mid ? build(mid + 1, r, m2 + 1, R) : 0;
if (l == r) return siz[st[l]] = 1, st[l];
return siz[x] = siz[ls[x]] + siz[rs[x]], x;
}
void rebuild(int &p, int L, int R) { tp = 0, pia(p), p = build(1, tp, L, R); }
int insert(int &p, int l, int r, node t) {
if (!p) { val[p = ++cnt] = t, V[p] = (l + r) >> 1; siz[p] = 1; return p; }
int mid = (l + r) >> 1, res = 0;
if (t == val[p]) return p;
if (t < val[p]) res = insert(ls[p], l, mid - 1, t);
else res = insert(rs[p], mid + 1, r, t);
siz[p] = siz[ls[p]] + siz[rs[p]] + 1;
if (max(siz[ls[p]], siz[rs[p]]) >= siz[p] * 0.73) rebuild(p, l, r);
return res;
}
}
char op[5];
int main() {
read(n), read(m); V[0] = val[0].ls = -1, SCT::insert(rt, 1, 1e9, node(0, 0));
for (int i = 1;i <= n; ++i) id[i] = 1; build(1, 1, n);
for (int i = 1, l, r, k;i <= m; ++i) {
scanf ("%s", op), read(l), read(r);
if (op[0] == 'C') {
read(k), id[k] = SCT::insert(rt, 1, 1e9, node(id[l], id[r]));
change(1, 1, n, k);
} else write(query(1, 1, n, l, r));
}
return 0;
}
/*
5 10
C 1 1 1
C 2 1 2
Q 1 2
C 4 4 4
C 5 5 5
Q 4 5
Q 3 3
C 4 2 3
C 4 4 4
Q 3 4
*/