• @bzoj



    @description@

    (简化版题意)我们定义一种新的“数”:要么是 0;要么是一个“数”对(这里的“数”指我们新定义的数) (l,r), 其中l和r也是“数”。

    每一个“数”如果不是0,其一定可以一直拆分直至不可拆分。

    “数” 的大小关系定义: 0最小,其余“数”按照“数”对第一项为第一关键字,第二项为第二关键字。

    维护一个由n个这样的“数”组成的序列,初始每个位置都是0。支持两种操作,将a[k]修改为(a[l],a[r]),或询问区间最小值。操作总数为m。

    原题传送门。

    @solution@

    基本思路很简单:给 “数” 一个动态维护的 hash 值,使得 hash 值的大小比较等价于 “数” 的大小比较。再用线段树维护 hash 值,单点修改 + 区间 min 即可。

    使用平衡树可以维护 “数” 的偏序集(为了方便,我们不把单个 0 放入平衡树),每次新创造的 “数” 可以利用 hash 值 O(nlogn) 插入平衡树中。

    那么这个 hash 值怎么选取呢?比较直观的选取方法是直接选结点在平衡树的 rank,不过发现不能很好地维护。

    另一种方法是选取根到当前结点的路径代表的 01 字符串:往左子树为 0,往右子树为 1。如果在字符串末尾加字符 1,两个字符串的字典序大小关系就可以表示 “数” 的大小关系(有些类似于 trie)。
    如果选取的是重量平衡树(如带旋转的 treap),就可以在 O(nlog^2n) 维护出这种 hash 值。

    要是能将字符串压成一个数,就可以去掉一个 log。这也就是网上的大部分题解的做法:我们将 hash 值改成一个实数。
    根分配权值区间 (0, 1);假如一个点分配权值区间 (l, r),则该点 hash 值为 (l + r) / 2,左子树分配 (l, m),右子树分配 (m, r)。
    因为树高 O(log),所以精度不会有问题。

    @accepted code@

    #include <cmath>
    #include <stack>
    #include <cstdio>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef unsigned int ui;
    #define mp make_pair
    #define fi first
    #define se second
    
    const double EPS = 1E-9;
    const int MAXN = 100000;
    const int MAXM = 500000;
    
    struct treap{
    	struct node{
    		ui pri; pair<node*, node*> val;
    		double f;
    		node *ch[2];
    	}pl[MAXM + 5], *ncnt, *NIL, *root;
    	typedef pair<node*, node*> Dnode;
    	int cmp(Dnode a, Dnode b) {
    		if( a.fi->f == b.fi->f && a.se->f == b.se->f )
    			return 0;
    		if( a.fi->f == b.fi->f )
    			return a.se->f < b.se->f ? -1 : 1;
    		else return a.fi->f < b.fi->f ? -1 : 1;
    	}
    	treap() {
    		ncnt = NIL = pl;
    		NIL->f = 0, NIL->ch[0] = NIL->ch[1] = NIL;
    		root = newnode(mp(NIL, NIL)), root->f = 0.5;
    	}
    	ui get_rand() {return ui(rand() << 16 | rand());}
    	node *newnode(Dnode k) {
    		node *p = (++ncnt);
    		p->val = k, p->ch[0] = p->ch[1] = NIL, p->pri = get_rand();
    		return p;
    	}
    	void rotate(node *y, node *x) {
    		int d = (y->ch[1] == x);
    		y->ch[d] = x->ch[!d], x->ch[!d] = y;
    	}
    	double le, ri;
    	node *find(double l, double r, node *&rt, Dnode p) {
    		if( rt == NIL ) {
    			rt = newnode(p), le = l, ri = r;
    			return rt;
    		}
    		else if( cmp(p, rt->val) == 0 )
    			return rt;
    		else {
    			double m = (l + r) / 2;
    			node *ret = (cmp(p, rt->val) < 0 ? find(l, m, rt->ch[0], p) : find(m, r, rt->ch[1], p));
    			if( ret->pri > rt->pri )
    				rotate(rt, ret), rt = ret, le = l, ri = r;
    			return ret;
    		}
    	}
    	void reget(double l, double r, node *x) {
    		if( x == NIL ) return ;
    		x->f = (l + r) / 2;
    		reget(l, x->f, x->ch[0]), reget(x->f, r, x->ch[1]);
    	}
    	node *find(Dnode p) {
    		le = ri = -1;
    		node *ret = find(0, 1, root, p);
    		if( le != -1 && ri != -1 ) reget(le, ri, ret);
    		return ret;
    	}
    }T1;
    
    struct type{
    	treap::node *a; int b; type() {}
    	type(treap::node *_a, int _b) : a(_a), b(_b) {}
    	friend bool operator < (type a, type b) {
    		return a.a->f == b.a->f ? (a.b > b.b) : a.a->f < b.a->f;
    	}
    };
    
    struct segtree{
    	#define lch (x << 1)
    	#define rch (x << 1 | 1)
    	
    	int le[4*MAXN + 5], ri[4*MAXN + 5]; type mx[4*MAXN + 5];
    	
    	void pushup(int x) {mx[x] = max(mx[lch], mx[rch]);}
    	void build(int x, int l, int r) {
    		le[x] = l, ri[x] = r;
    		if( l == r ) {
    			mx[x] = type(T1.NIL, l);
    			return ;
    		}
    		int m = (l + r) >> 1;
    		build(lch, l, m), build(rch, m + 1, r);
    		pushup(x);
    	}
    	void modify(int x, int p, treap::node *k) {
    		if( le[x] == ri[x] ) {
    			mx[x] = type(k, p);
    			return ;
    		}
    		int m = (le[x] + ri[x]) >> 1;
    		if( p <= m ) modify(lch, p, k);
    		else modify(rch, p, k);
    		pushup(x);
    	}
    	type query(int x, int l, int r) {
    		if( l > ri[x] || r < le[x] )
    			return type(T1.NIL, MAXN + 5);
    		if( l <= le[x] && ri[x] <= r )
    			return mx[x];
    		return max(query(lch, l, r), query(rch, l, r));
    	}
    }T2;
    
    int n, m;
    
    int main() {
    	scanf("%d%d", &n, &m);
    	srand(20041112), T2.build(1, 1, n);
    	for(int i=1;i<=m;i++) {
    		char op[2]; scanf("%s", op);
    		if( op[0] == 'C' ) {
    			int l, r, k; scanf("%d%d%d", &l, &r, &k);
    			treap::node *L = T2.query(1, l, l).a, *R = T2.query(1, r, r).a;
    			treap::node *K = T1.find(mp(L, R));
    			T2.modify(1, k, K);
    		}
    		else {
    			int l, r; scanf("%d%d", &l, &r);
    			printf("%d
    ", T2.query(1, l, r).b);
    		}
    	}
    }
    /*
    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
    
    2
    4
    3
    3
    */
    

    @details@

    一个小细节:线段树里面不存储 hash 值本身,而存储平衡树对应结点指针会更好维护。

  • 相关阅读:
    JS网页顶部进度条demo
    C# Emit动态代理生成一个实体对象
    C# 表达式树demo
    C# Thread挂起线程和恢复线程
    JS网页加载进度条
    android 布局
    工程发布问题总结
    jquery集锦
    部署maven到服务器
    WebView显示的网页在大分辨率屏下被放大--解决方案
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12420874.html
Copyright © 2020-2023  润新知