• [湖北省队互测2014] 没有人的算术 (非题解)


    只有跳蚤的算术。
    题面题解在此
    正解用了替罪羊树哎

    推荐阅读
    洛谷日报:替罪羊树
    《­重量平衡树和后缀平衡树在信息学奥赛中的应用》—— clj


    初步思路

    题面就是赋值和区间最大值。

    区间最大值明显用线段树维护, 重要的是如何比较两个 “数” 的大小。

    可以直接用平衡树维护全序集(平衡树就是用来维护全序集的东西), 这样的话比较两个“数”大小时就可以通过比较其在平衡树里的排名来比较大小。

    注意到每个新 “数” 都由平衡树里的老 “数” 合成, 所以总是可以方便地比较老 “数” 与 新 “数” 的大小。
    具体地说, 每当 (a[k]) 被赋值为 (new = (a[l], a[r])) 时, 将 (new) 插入到平衡树中, 插入过程中的大小比较可以套一个平衡树里的查找((a[l]、a[r]) 的排名预先找出来, 与当前节点比较的时候直接查找当前节点 ((l,r)) 的排名), 一次插入的总复杂度为 (O(log^2n))

    由于在线段树里修改也要比较大小,所以在线段树里修改的复杂度也是 (log^2) 的。


    全序集的同构?
    之前用平衡树维护的 “数” 的全序集 (S) 中, 若有 (x,y in S)(x < y),则 (rank(x) < rank(y)), 反过来也成立。
    如果定义一个函数 (infty(x)) 使得若有 若有 (x,y in S)(x < y), 则必有 (infty(x) < infty(y)) 且反过来也成立, 那么比较两个“数”大小时, 比较 (rank) 的过程就可以换为比较 (infty), 那么就不用每次都在插入里套一个查找, 复杂度就变为 (O(log n * 比较infty的复杂度))
    比较 (infty) 的复杂度自然最好是 (O(1))

    满分做法
    思路来自近代神犇 ( ext{vfleaking})

    对于一个平衡树中的节点, 给它一个开区间 ((l,r)), 区间中点 (mid = frac{l+r}2), 则它的左儿子的区间为 ((l,mid)), 右节点的区间为 ((mid,r))

    对每个节点定义一个函数 (infty), 若一个节点 (x) 的区间为 ((l,r))
    (infty(x) = frac{l+r}2)

    可以简单地证明通过 (infty) 比较平衡树中的两个“数”的大小与通过 (rank) 比较平衡树中两个“数”的大小, 结果是等价的。

    然后就可以 (O(1)) 比较两个 “数” 的大小了。

    但是有一个问题, 就是 (infty) 是自上到下计算的, 如果使用旋转平衡树的话, 维护 (infty) 会很麻烦。

    所以这时候就要用替罪羊树啦, 无旋, 好写,复杂度好证。


    代码设计
    思路来自 (Marser)

    • 主体是结构体 “数”, 有标号, 每个 “数” 要记录自己的 (infty) 值 和 组成其的两个数的标号。
    • 平衡树直接维护 “数”。
    • 线段树维护一个序列, 序列中每个数都是某个 “数” 的标号。

    以下代码(Luogu) 数据能过。(懒得自己测原数据了qwq)

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 5e5 + 15;
    int n,m;
    int pos[N];
    
    const double al = 0.7;
    int top;
    //double tl, tr;
    
    int tot;
    double val[N];
    struct node {
    	int l,r;
    	bool operator<(node p) {
    		return val[l]==val[p.l]?val[r]<val[p.r]:val[l]<val[p.l];
    	}
    	bool operator==(node p) {
    		return l==p.l && r==p.r;
    	}
    } nodes[N];
    
    
    int rt, siz[N], ch[N][2];
    inline void ud(int x) {
    	siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];
    }
    
    int len,arr[N];
    void pia(int x) {
    	if(!x) return;
    	pia(ch[x][0]);
    	arr[++len]=x;
    	pia(ch[x][1]);
    }
    void build(int &x,int l,int r,double L,double R) {
    	if(l>r) return (void)(x=0);
    	int mid=(l+r)>>1;
    	double MID=(L+R)/2;
    	x=arr[mid];
    	val[x]=MID;
    	siz[x]=1;
    	build(ch[x][0],l,mid-1,L,MID);
    	build(ch[x][1],mid+1,r,MID,R);
    	ud(x);
    }
    void rebuild(int &x,double L,double R) {
    	len=0;
    	pia(x);
    	build(x,1,len,L,R);
    	top=0;
    }
    int ins(int &x,double L,double R,node a) {
    	double MID=(L+R)/2;
    	if(!x) {
    		x=++tot;
    		siz[x]=1;
    		val[x]=MID;
    		nodes[x]=a;
    		return x;
    	}
    	if(nodes[x]==a) return x;
    	int res = 0;
    	if(a<nodes[x]) res=ins(ch[x][0],L,MID,a);
    	else           res=ins(ch[x][1],MID,R,a);
    	ud(x);
    //	if(max(siz[ch[x][0]], siz[ch[x][1]]) > siz[x] * al) top=x, tl=L, tr=R;
    	if(max(siz[ch[x][0]],siz[ch[x][1]])<siz[x]*al) {
    		if(top) {
    			if(ch[x][0]==top) rebuild(ch[x][0],L,MID);
    			else rebuild(ch[x][1],MID,R);
    			top=0;
    		}
    	} else top=x;
    	return res;
    }
    
    
    namespace seg {
    	int s[N];
    #define ls (u<<1)
    #define rs (u<<1|1)
    	void upd(int u,int l,int r,int k) {
    		if(l==r) return (void)(s[u]=l);
    		int mid=(l+r)>>1;
    		if(k<=mid) upd(ls,l,mid,k);
    		else upd(rs,mid+1,r,k);
    		s[u] = (val[pos[s[ls]]]<val[pos[s[rs]]]) ? s[rs] : s[ls];
    	}
    	int ques(int u,int l,int r,int x,int y) {
    		if(x<=l&&r<=y) return s[u];
    		int mid=(l+r)>>1;
    		if(y<=mid) return ques(ls,l,mid,x,y);
    		if(x>mid) return ques(rs,mid+1,r,x,y);
    		int pl=ques(ls,l,mid,x,y), pr=ques(rs,mid+1,r,x,y);
    		if(val[pos[pl]]<val[pos[pr]]) return pr;
    		else return pl;
    	}
    #undef ls
    #undef rs
    }
    inline void change(int k) {
    	seg::upd(1,1,n,k);
    }
    inline int ques(int l,int r) {
    	return seg::ques(1,1,n,l,r);
    }
    int main() {
    	scanf("%d%d", &n, &m);
    
    	val[0]=-1;
    	ins(rt,0,1,(node) {
    		0,0
    	});
    	for(int i=1; i<=n; ++i) pos[i]=tot;
    	for(int i=1; i<=n; ++i) change(i);
    
    	char s[3];
    	int l,r,k;
    	while(m--) {
    		scanf("%s", s);
    		if(s[0]=='C') {
    			scanf("%d%d%d", &l,&r,&k);
    			pos[k] = ins(rt, 0, 1, (node) {
    				pos[l],pos[r]
    			});
    			//if(SGT::top)SGT::rebuild(root,0,1),SGT::top=0;
    			if(top) rebuild(rt,0,1);
    			change(k);
    		} else {
    			scanf("%d%d", &l,&r);
    			printf("%d
    ", ques(l,r));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Android开发学习--RecycleView入门
    Android开发学习--MVP模式入门
    Android开发学习--ViewPager使用入门
    LocalDB
    Asp.Net Mvc5新特性
    Asp.net MVC4 捆绑和压缩
    实施双工通信框架:SignalR
    Razor 语法
    Action Result
    操作Action
  • 原文地址:https://www.cnblogs.com/tztqwq/p/13150698.html
Copyright © 2020-2023  润新知