#替罪羊树,线段树#洛谷 6272 [湖北省队互测2014] 没有人的算术

题目传送门


分析

其实抛开 \(a[k]\) 的类型不谈,外层相当于是线段树的单点修改和区间查询。

主要是比较大小的问题,不妨用一棵平衡树维护偏序关系,用一个数对维护,

数对的每一个值对应了一个赋值,赋值由平衡树的划分决定,

但是如果树高过高就会影响精度,而旋转难以维护赋值,所以需要替罪羊树直接拍扁重建。


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=600011;
const double alp=0.75;
int n,Q,w[N],a[N]; double b[N];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
bool chose(pair<int,int>wl,pair<int,int>wr){return b[wl.first]==b[wr.first]?b[wl.second]>=b[wr.second]:b[wl.first]>b[wr.first];}
struct ScapeGoat_Tree{
	int siz[N],son[N][2],root,tot=0,fat[N],stac[N],TOP; pair<int,int>w[N];
	void BUILD(){siz[1]=root=tot=1,w[1]=make_pair(0,0),b[1]=165;}
	bool balance(int x){return alp*siz[x]>=(max(siz[son[x][0]],siz[son[x][1]]));}
	void recycle(int x){
		if (son[x][0]) recycle(son[x][0]);
		stac[++TOP]=x;
		if (son[x][1]) recycle(son[x][1]);
	}
	int build(int l,int r,double L,double R){
		if (l>r) return 0;
		int mid=(l+r)>>1,x=stac[mid];
		double MID=(L+R)/2; b[x]=MID;
		fat[son[x][0]=build(l,mid-1,L,MID)]=x;
		fat[son[x][1]=build(mid+1,r,MID,R)]=x;
		siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
		return x;
	}
	void rebuild(int x,double L,double R){
		TOP=0; recycle(x);
		int fa=fat[x],wh=son[fat[x]][1]==x;
		int now=build(1,TOP,L,R);
		fat[son[fa][wh]=now]=fa;
		if (root==x) root=now;
	}
	int Insert(pair<int,int>x){
		double L=0,R=330;
		int now=root;
		while (1){
			if (b[w[now].first]==b[x.first]&&b[w[now].second]==b[x.second]) return now;
			bool wh=chose(x,w[now]);
			if (wh) L=(L+R)/2;
				else R=(L+R)/2;
			if (!son[now][wh]){
				fat[son[now][wh]=++tot]=now;
				w[tot]=x,b[tot]=(L+R)/2;
				break;
			}else now=son[now][wh];
		}
		for (int i=tot;i;i=fat[i]) ++siz[i];
		int UP=0; double UPL=-1,UPR=-1;
		for (int i=tot;i;i=fat[i]){
			if (!balance(i)) UP=i,UPL=L,UPR=R;
			if (son[fat[i]][1]==i) L=L*2-R;
			    else R=R*2-L;
		}
		if (UP) rebuild(UP,UPL,UPR);
		return tot;
	}
}Tre;
int pup(int wl,int wr){return chose(Tre.w[a[wl]],Tre.w[a[wr]])?wl:wr;}
void build(int k,int l,int r){
	if (l==r){
		w[k]=l;
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	w[k]=pup(w[k<<1],w[k<<1|1]);
}
void update(int k,int l,int r,int x){
	if (l==r){
		w[k]=x;
		return;
	}
	int mid=(l+r)>>1;
	if (x<=mid) update(k<<1,l,mid,x);
	    else update(k<<1|1,mid+1,r,x);
	w[k]=pup(w[k<<1],w[k<<1|1]);
}
int query(int k,int l,int r,int x,int y){
	if (l==x&&r==y) return w[k];
	int mid=(l+r)>>1;
	if (y<=mid) return query(k<<1,l,mid,x,y);
	else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
	else{
		int wl=query(k<<1,l,mid,x,mid),wr=query(k<<1|1,mid+1,r,mid+1,y);
		return pup(wl,wr);
	}
}
int main(){
	n=iut(),Q=iut(),b[0]=-1,Tre.BUILD();
	for (int i=1;i<=n;++i) a[i]=1;
	build(1,1,n);
	for (int i=1;i<=Q;++i){
		char ch=getchar();
		while (!isalpha(ch)) ch=getchar();
		int x=iut(),y=iut();
		if (ch=='C'){
			int z=iut();
			a[z]=Tre.Insert(make_pair(a[x],a[y]));
			update(1,1,n,z);
		}else print(query(1,1,n,x,y)),putchar(10);
	}
	return 0;
}
posted @ 2025-06-15 10:38  lemondinosaur  阅读(23)  评论(0)    收藏  举报