suxxsfe

一言(ヒトコト)

P6272 没有人的算术

https://darkbzoj.tk/problem/3600
https://www.luogu.com.cn/problem/P6272
替罪羊树动态标号+线段树
这个东西应该是叫动态标号吧,反正我看别的大佬是这样说的

题意:
定义了新的一种数,递归的定义为:\((x,y)\)\(0\),其中 \(x,y\) 也是这种数
对于两个这种数大小的比较:若 \(a<b\)\(a=b,c<d\),则 \((a,b)<(c,d)\);若 \(a=b,c=d\)\((a,b)=(c,d)\)

给定一个长度为 \(n\) 的这种数的序列 \(a\),初始全是 \(0\),有 \(m\) 个操作,分为两种:

  • C l r k:将 \(a_k\) 赋值为 \((a_l,a_r)\)
  • Q l r:查询 \(a_l,\ldots a_r\) 中最小的数,如果存在多个同样小的数,将编号最小的视为最小。输出这个编号

\(n\le 10^5,m\le 5\cdot 10^5\)


如果我们能较快的比较两个数的大小,那直接一个线段树单点查询区间维护最大就完了
但如果按照定义来比较,显然复杂度爆掉,那么下面考虑如何更快的比较

可以把每一个题目里的数都用一个实数来表达

首先要解决的是,如何确定这个实数的值。
因为我们不知道后面插入的数有多少比他大,多少比他小,所以可以把它们放到一颗二叉树上,每个节点代表一个权值的区间 \((l,r)\),然后这个节点上的数对权值就是 \(\dfrac{l+r}{2}\)
当插入一个数对时,如果这个数对比当前节点的数对小,就近左子树,权值区间 \((l,\frac{l+r}{2})\),反之进入右子树,权值区间 \((\frac{l+r}{2},r)\)
一直递归到节点为空,或当前节点存在且它代表的数对和插入的数对相等,就返回这个节点

但这样,如果退化成一个链,那每次除以二,精度还是不够用
而这里又涉及权值的区间的问题,不能用基于旋转或分裂合并的平衡树,所以应该用替罪羊树
每次暴力重构的时候,重新为每个节点赋上权值的区间,还有代表这个节点数对的实数

下面就是实现细节上的问题了
至于如何在一个节点保存一个数对,我们在节点的结构体里存两个指针,由于一个数对实际上是由两个保存在其它节点的数对组成,所以这两个指针也就分别指向组成它的两个节点
此外,还需要一个 pos 指针数组,\(pos_i\) 指向 \(a_i\) 所对应的数对在替罪羊树上的节点
然后就可以借助这个数组在线段树上进行 pushup 了

只于一开始所有数都为 \(0\),就先往替罪羊树里插入一个那两个指针都是 null 的节点,也就是根,然后所有 \(pos_i\) 一开始都指向根就行了

屎山代码(写了好几个小时,而且是真的丑):

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 500005
int n,m;
struct GOAT{
	#define alpha 0.7
	struct tr;
	struct data{
		tr *l,*r;
		inline int operator <(const data &x){
			return l->a==x.l->a?r->a<x.r->a:l->a<x.l->a;
		}
		inline int operator==(const data &x){
			return l->a==x.l->a&&r->a==x.r->a;
		}
	};
	struct tr{
		tr *ls,*rs;
		int size;
		data val;
		double a;
	}*root,*null,**badtag,dizhi[N],*nodes[N],*pos[N];
	double badtagL,badtagR;
	int tot,node;
	inline void init(){
		null=&dizhi[0];
		null->val=(data){0,0};
		null->size=0;
		root=null;
	}
	inline int isbad(tr *tree){
		return tree->ls->size>tree->size*alpha+5||tree->rs->size>tree->size*alpha+5;
	}
	void dfs(tr *tree){
		if(tree->ls!=null) dfs(tree->ls);
		nodes[++node]=tree;
		if(tree->rs!=null) dfs(tree->rs);
	}
	tr* build(int l,int r,double L,double R){
		if(l>r) return null;
		double MID=(L+R)/2;
		if(l==r){
			nodes[l]->ls=nodes[l]->rs=null;
			nodes[l]->size=1;
			nodes[l]->a=MID;
			return nodes[l];
		}
		int mid=(l+r)>>1;
		nodes[mid]->ls=build(l,mid-1,L,MID);nodes[mid]->rs=build(mid+1,r,MID,R);
		nodes[mid]->size=1+nodes[mid]->ls->size+nodes[mid]->rs->size;
		nodes[mid]->a=MID;
		return nodes[mid];
	}
	inline void rebuild(tr *&tree){
		node=0;dfs(tree);
		tree=build(1,node,badtagL,badtagR);
	}
	tr* insert(tr *&tree,double l,double r,data k){
		double mid=(l+r)/2;
		if(tree==null){
			tree=&dizhi[++tot];
			tree->ls=tree->rs=null;
			tree->val=k;
			tree->a=mid;
			tree->size=1;
			return tree;
		}
		if(k==tree->val) return tree;
		tree->size++;
		tr* ret;
		if(k<tree->val) ret=insert(tree->ls,l,mid,k);
		else ret=insert(tree->rs,mid,r,k);
		if(isbad(tree)) badtag=&tree,badtagL=l,badtagR=r;
		return ret;
	}
	inline tr* Insert(data k){
		badtag=NULL;
		tr *ret=insert(root,1,1e9,k);
		if(badtag) rebuild(*badtag);
		return ret;
	}
	#undef alpha
}B;
struct SEG{
	struct tr{
		tr *ls,*rs;
		int pos;
	}dizhi[N],*root=&dizhi[0];
	int tot;
	void build(tr *tree,int l,int r){
		if(l==r) return tree->pos=l,void();
		tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
		int mid=(l+r)>>1;
		build(tree->ls,l,mid);build(tree->rs,mid+1,r);
		tree->pos=l;
	}
	void change(tr *tree,int l,int r,int pos){
		if(l==r) return;
		int mid=(l+r)>>1;
		if(pos<=mid) change(tree->ls,l,mid,pos);
		else change(tree->rs,mid+1,r,pos);
		tree->pos=B.pos[tree->ls->pos]->a>=B.pos[tree->rs->pos]->a?tree->ls->pos:tree->rs->pos;
	}
	int get(tr *tree,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr) return tree->pos;
		int mid=(l+r)>>1;
		if(qr<=mid) return get(tree->ls,l,mid,ql,qr);
		if(ql>mid) return get(tree->rs,mid+1,r,ql,qr);
		int L=get(tree->ls,l,mid,ql,qr),R=get(tree->rs,mid+1,r,ql,qr);
		return B.pos[L]->a>=B.pos[R]->a?L:R;
	}
}A;
int main(){
	n=read();m=read();
	B.init();
	B.Insert((GOAT::data){B.null,B.null});
	A.build(A.root,1,n);
	for(reg int i=1;i<=n;i++) B.pos[i]=B.root;
	reg int l,r,k;char c;
	while(m--){
		c=getchar();
		while(c!='Q'&&c!='C') c=getchar();
		l=read();r=read();
		if(c=='C'){
			k=read();
			B.pos[k]=B.Insert((GOAT::data){B.pos[l],B.pos[r]});
			A.change(A.root,1,n,k);
		}
		else printf("%d\n",A.get(A.root,1,n,l,r));
	}
	return 0;
}
posted @ 2020-07-28 18:08  suxxsfe  阅读(120)  评论(0编辑  收藏  举报