zoukankan      html  css  js  c++  java
  • 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
    
    */
    
  • 相关阅读:
    设计模式学习总结
    算法时间复杂度和空间复杂度表示
    SQLite简单使用
    接口,组合和继承的想法
    二叉树的学习
    Oracle 常用命令大汇总
    Oracle 最常用功能函数经典汇总
    oracle 常用command
    历史最牛演讲:Oracle总裁Yale演讲全文中英文对照
    深入abstract class和interface
  • 原文地址:https://www.cnblogs.com/Hs-black/p/14346643.html
Copyright © 2011-2022 走看看