#替罪羊树,权值线段树,树套树,平衡树套线段树#洛谷 4278 带插入区间K小值

题目

带插入的 Dynamic Ranking,强制在线。


分析

由于插入导致下标发生改变,因此外层的树状数组应该替换为平衡树,每次对根到该点的路径上的权值线段树加一,

而旋转难以维护内层的线段树,因此使用替罪羊树直接拍扁重建,这里为了卡常去掉了垃圾回收而是使用定期重构,

当线段树结点数超过阈值就把整个替罪羊树拍扁重建,当然此时 \(\alpha\) 就得调大点,评论区给的是 \(0.955\)

还有什么递归转非递归之类的就不多说了,反正参数都很神秘就是了,唉。


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin)),p1==p2?EOF:*p1++)
using namespace std;
const int N=70001,M=24600000; char buf[1<<22],puf[1<<22],*p1,*p2; int nowp=-1;
const double alp=0.955; int n,a[N],rt[N],St[N],ToP,st[N],Top;
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;
}
void Flush(){fwrite(puf,1,nowp+1,stdout),nowp=-1;}
void Putchar(char x){
	if (nowp==(1<<22)) Flush();
	puf[++nowp]=x;
}
void print(int ans){
	char dig[10]; int len=-1;
	do{
		dig[++len]=ans%10+48,ans/=10; 
	}while (ans);
	while (len>=0) Putchar(dig[len--]);
}
struct Segment_Tree{
	int w[N<<10],cnt,TOP,ls[N<<10],rs[N<<10];
	void update(int &rt,int l,int r,int x,int y){
		if (!rt) rt=++cnt;
		w[rt]+=y;
		if (l==r) return;
		int mid=(l+r)>>1;
		if (x<=mid) update(ls[rt],l,mid,x,y);
			else update(rs[rt],mid+1,r,x,y);
	}
	void Merge(int &fi,int se,int l,int r){
		if (!fi&&!se) return;
		int Fi=++cnt;
		w[Fi]=w[fi]+w[se];
		if (l==r){
			fi=Fi;
			return;
		}
		int mid=(l+r)>>1;
		Merge(ls[Fi]=ls[fi],ls[se],l,mid);
		Merge(rs[Fi]=rs[fi],rs[se],mid+1,r);
		fi=Fi;
	}
	void recycle(int rt){
		if (ls[rt]) recycle(ls[rt]);
		if (rs[rt]) recycle(rs[rt]);
		w[rt]=ls[rt]=rs[rt]=0;
	}
}tre;
struct ScapeGoat_Tree{
	int siz[N],son[N][2],root,tot=0,fat[N],stac[N],TOP;
	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 (rt[x]) tre.recycle(rt[x]),rt[x]=0;
		if (son[x][1]) recycle(son[x][1]);
	}
	int build(int l,int r){
		if (l>r) return 0;
		int mid=(l+r)>>1,x=stac[mid];
		fat[son[x][0]=build(l,mid-1)]=x;
		fat[son[x][1]=build(mid+1,r)]=x;
		tre.Merge(rt[x]=rt[son[x][0]],rt[son[x][1]],0,N);
		tre.update(rt[x],0,N,a[x],1);
		siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
		return x;
	}
	void rebuild(int x){
		TOP=0; recycle(x);
		int fa=fat[x],wh=son[fat[x]][1]==x;
		int now=build(1,TOP);
		fat[son[fa][wh]=now]=fa;
		if (root==x) root=now;
	}
	void fetch(int kth,int y){
		int now=root;
		while (1){
			if (kth<=siz[son[now][0]]) now=son[now][0];
			else if (kth>siz[son[now][0]]+1) kth-=siz[son[now][0]]+1,now=son[now][1];
		        else break;
		}
		for (int i=now;i;i=fat[i]){
			tre.update(rt[i],0,N,a[now],-1);
			tre.update(rt[i],0,N,y,1);
		}
		a[now]=y;
	}
	void Insert(int kth){
		int now=root,renew=++tot,wh;
		while (1){
			++siz[now];
            tre.update(rt[now],0,N,a[tot],1);
			if (kth>siz[son[now][0]]+1) kth-=siz[son[now][0]]+1,wh=1;
			    else wh=0;
			if (son[now][wh]) now=son[now][wh];
			    else {fat[son[now][wh]=renew]=now; break;}
		}
		++siz[renew];
        tre.update(rt[renew],0,N,a[tot],1);
		int UP=0;
		for (int i=renew;i;i=fat[i]) if (!balance(i)) UP=i;
		if (UP) rebuild(UP);
	}
	void query(int now,int l,int r,int x,int y){
		if (l==x&&r==y){
			St[++ToP]=rt[now];
			return;
		}
		int mid=l+siz[son[now][0]];
		if (y<mid) query(son[now][0],l,mid-1,x,y);
		else if (x>mid) query(son[now][1],mid+1,r,x,y);
		else{
			st[++Top]=a[now];
			if (x<mid) query(son[now][0],l,mid-1,x,mid-1);
			if (mid<y) query(son[now][1],mid+1,r,mid+1,y);
		}
	}
}Tre;
void Print(int now){
	if (Tre.son[now][0]) Print(Tre.son[now][0]);
	printf("%d ",a[now]);
	if (Tre.son[now][1]) Print(Tre.son[now][1]);
}
int query(int x,int y,int kth){
	int l=0,r=N; Top=ToP=0;
	Tre.query(Tre.root,1,n,x,y);
	while (l<r){
		int mid=(l+r)>>1,sum=0;
		for (int i=1;i<=ToP;++i) sum+=tre.w[tre.ls[St[i]]];
		for (int i=1;i<=Top;++i) if (l<=st[i]&&st[i]<=mid) ++sum;
		if (sum>=kth){
			r=mid;
			for (int i=1;i<=ToP;++i) St[i]=tre.ls[St[i]];
		}else{
			kth-=sum,l=mid+1;
			for (int i=1;i<=ToP;++i) St[i]=tre.rs[St[i]];
		}
	}
	return l;
}
int main(){
	n=iut();
	for (int i=1;i<=n;++i) a[i]=iut(),Tre.stac[i]=i;
	Tre.root=Tre.build(1,Tre.tot=n);
	for (int Q=iut(),lans=0;Q;--Q){
		char ch=getchar();
		while (!isalpha(ch)) ch=getchar();
		int x=iut()^lans,y=iut()^lans;
		switch (ch){
			case 'Q':{
				print(lans=query(x,y,iut()^lans));
				Putchar(10);
				break;
			}
			case 'M':{
				Tre.fetch(x,y);
				break;
			}
			case 'I':{
				a[++n]=y;
				Tre.Insert(x);
				break;
			}
		}
		if (tre.cnt>M) tre.cnt=0,Tre.rebuild(Tre.root);
	}
	Flush();
	return 0;
}
posted @ 2025-06-18 01:24  lemondinosaur  阅读(14)  评论(0)    收藏  举报