• BZOJ3600 没有人的算术(替罪羊树,线段树)


    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
    
    */
    
  • 相关阅读:
    黄宗禹9.11作业
    黄宗禹第一次作业
    9.11
    9.18
    计算平均速度
    圆的周长与面积
    JAVA 作业
    9.11
    9.25
    计算平均速度题
  • 原文地址:https://www.cnblogs.com/Hs-black/p/14346643.html
Copyright © 2020-2023  润新知