替罪羊树

基操

  • 维护二叉搜索树,当其不平衡时重拍

模板1

Luogu P3369 【模板】普通平衡树

其他都一样,但删除由于二叉搜索树本来就不行,所以打标记

打标记后,如果有重复元素,删除要删第几个元素,而非某个元素

#include<bits/stdc++.h>
const double aph=0.75;
using namespace std;

const int N=1e5+5;
int cnt,s[N],c[N],a[N],ls[N],rs[N],rt;
bool fl[N];
inline bool bad(int p) {
	return aph*s[p]<=max(s[ls[p]],s[rs[p]]); 
}
inline void up(int p) {
	s[p]=s[ls[p]]+s[rs[p]]+(!fl[p]);
}
int tot; 
void bld(int &p,int l,int r) {
	if(l>r) {
		p=0; return;
	}
	int mid=l+r>>1;
	p=a[mid],bld(ls[p],l,mid-1),bld(rs[p],mid+1,r);
	up(p);
}
void dfs(int p) {
	if(!p) return;
	dfs(ls[p]);
	if(!fl[p]) a[++tot]=p;
	dfs(rs[p]);
}
void rebld(int &p) {
	tot=0;
	dfs(p);
	bld(p,1,tot);
}
void ins(int &p,int x) {
	if(!p) {
		p=++cnt,c[p]=x,s[p]=1;
		return;
	}
	if(x<=c[p]) ins(ls[p],x);
		else ins(rs[p],x);
	up(p);
	if(bad(p)) rebld(p);
}
void del(int &p,int k) {
	int u=rt;
	while(u) {
		s[u]--;
		if(!fl[u]&&k==s[ls[u]]+1) {
			fl[u]=1; return;
		}
		if(k<=s[ls[u]]) u=ls[u];
			else {
				k-=s[ls[u]]+(!fl[u]);
				u=rs[u];
			}
	}
}

inline int rnk(int k) {
	int u=rt,ret=1;
	while(u) {
		if(k<=c[u]) u=ls[u];
			else {
				ret+=s[ls[u]]+(!fl[u]);
				u=rs[u];
			}
	}
	return ret;
}

inline int kth(int k) {
	int u=rt;
	while(u) {
		if(!fl[u]&&k==s[ls[u]]+1) return c[u];
		if(k<=s[ls[u]]) u=ls[u];
			else {
				k-=s[ls[u]]+(!fl[u]);
				u=rs[u];
			}
	}
}

int main(){
	int T; scanf("%d",&T); 
	while(T--) {
		int op,x; scanf("%d%d",&op,&x);
		if(op==1) ins(rt,x);
		if(op==2) del(rt,rnk(x));
		if(op==3) printf("%d\n",rnk(x));
		if(op==4) printf("%d\n",kth(x));
		if(op==5) printf("%d\n",kth(rnk(x)-1));
		if(op==6) printf("%d\n",kth(rnk(x+1)));
	}
	return 0;
}

例题1

Luogu P4036 [JSOI2008]火星人

后缀数组?NO,我还能用Hash!!!

Hash直接自然溢出(好写精度还高)

插入,修改,区间和,想到平衡树,在序列化作的平衡树上维护Hash值,KO

节点维护子树的hash值,查询时类似线段树,但最好弄成2个全局变量,一个记当前是第几个,还有一个记hash值,好写多了

重构代码,不愧是我,数据结构一定要脑子清楚

#include<bits/stdc++.h>
#define ll unsigned long long
const double aph=0.75;
const int ji=27;
using namespace std;

const int N=1e5+5;
int n,b[N],tot;
char st[N],op[10];
	int cnt,sz[N],ls[N],rs[N],c[N],rt;
	ll jc[N],s[N];
	inline void up(int p) {
		s[p]=jc[sz[ls[p]]+1]*s[rs[p]]+jc[sz[ls[p]]]*c[p]+s[ls[p]];
		sz[p]=sz[ls[p]]+sz[rs[p]]+1;
	}
	void bld(int &p,int l,int r) {
		if(l>r) {
			p=0; return;
		}
		int mid=l+r>>1; p=b[mid];
		bld(ls[p],l,mid-1),bld(rs[p],mid+1,r);
		up(p);
	}
	void init() {
		cnt=n;
		jc[0]=1;
		for(int i=1;i<=100000;i++) {
			jc[i]=jc[i-1]*ji;
		}
		for(int i=1;i<=n;i++) {
			b[i]=i; c[i]=st[i]-'a'+1;
		}
		bld(rt,1,n);
	}
	void upd(int p,int x,int k) {
		if(sz[ls[p]]+1==x) {
			c[p]=k; up(p);
			return;
		}
		if(sz[ls[p]]>=x) upd(ls[p],x,k);
			else upd(rs[p],x-sz[ls[p]]-1,k);
		up(p);
	}
	void dfs(int p) {
		if(ls[p]) dfs(ls[p]);
		b[++tot]=p;
		if(rs[p]) dfs(rs[p]);
	}
	void rebld(int &p) {
		tot=0; dfs(p);
		bld(p,1,tot);
	}
	void ins(int &p,int x,int k) {
		if(!p) {
			p=++cnt; c[p]=k; up(p);
			return;
		}
		if(aph*sz[p]<=max(sz[ls[p]],sz[rs[p]])) {
			rebld(p);
		}
		if(sz[ls[p]]>=x) ins(ls[p],x,k);
			else ins(rs[p],x-sz[ls[p]]-1,k);
		up(p);
	}
	int sum; ll ret;
	void ask(int p,int l,int r,int x,int y) {
		if(l>r||x>y) return;
		if(l==x&&r==y) {
			ret+=jc[sum]*s[p];
			sum+=sz[p];
			return;
		}
		int mid=l+sz[ls[p]]; 
		if(y<mid) {
			ask(ls[p],l,mid-1,x,y);
			return;
		}
		if(x>mid) {
			ask(rs[p],mid+1,r,x,y);
			return;
		}
		ask(ls[p],l,mid-1,x,mid-1);
		ret+=jc[sum]*c[p],sum++; 
		ask(rs[p],mid+1,r,mid+1,y);
	}
	inline bool check(int x,int y,int mid) {
		sum=0; ret=0;
		ask(rt,1,n,x,x+mid-1);
		ll s1=ret; 
		sum=0; ret=0;
		ask(rt,1,n,y,y+mid-1);
		ll s2=ret; ret=0;
		return s1==s2;
	}

int main(){
	int T; scanf("%s%d",st+1,&T); n=strlen(st+1);
	init();
	while(T--) {
		scanf("%s",op);
		if(op[0]=='Q') {
			int x,y; scanf("%d%d",&x,&y);
			if(x>y)swap(x,y);
			int l=0,r=n-y+1,mid,ans=0;
			while(l<=r) {
				int mid=l+r>>1;
				if(check(x,y,mid)) {
					ans=mid,l=mid+1;
				} else r=mid-1;
			}
			printf("%d\n",ans); 
		} else if(op[0]=='R') {
			int x; scanf("%d%s",&x,op);
			upd(rt,x,op[0]-'a'+1);
		} else {
			int x; scanf("%d%s",&x,op);
			ins(rt,x,op[0]-'a'+1);
			++n;
		}
	}
	return 0;
}

例题2

Luogu P6272 [湖北省队互测2014]没有人的算术

如果每个东西都能用实数表示,这无疑是线段树的模板题

考虑如何表示

神仙想法:按所谓的偏集大小为键值维护一棵二叉搜索树,每个节点维护一个存在的实数范围\([l,r]\),该点自然取中间值\(mid\),左儿子取集合\([l,mid]\),右儿子取集合\([mid,r]\)

这样如果树大致是均匀的,则树高是\(lg\)级别,精度有保障,显然无法翻转,需要暴力重构

所以替罪羊树,为方便比较,替罪羊树上的节点不仅要记节点的值,还要记节点的构成单位(也是节点,防止重构后关系改变),同理数列上记的也是替罪羊树上的节点,重构只会改变数值,不会改变大小关系

PS:又tmd重构了代码,要不以后写两遍?

#include<bits/stdc++.h>
#define db long double
const db INF=1e18;
const db aph=0.75;
using namespace std;

const int N=1e6+5;
int n,a[N],sz[N],ls[N],rs[N],cnt,rt;
db c[N];
char op[10];
struct A{int u,v; }val[N];
bool operator == (A i,A j) {
	return i.u==j.u&&i.v==j.v; 
}
bool operator <(A i,A j) {
	return c[i.u]<c[j.u]||c[i.u]==c[j.u]&&c[i.v]<c[j.v];
}

inline void up(int p) {
	sz[p]=sz[ls[p]]+sz[rs[p]]+1;
}
int tot,b[N];
void bld(int &p,int l,int r,db x,db y) {
	if(l>r) {
		p=0; return;
	}
	int mid=l+r>>1; p=b[mid],c[p]=(x+y)/2;
	bld(ls[p],l,mid-1,x,c[p]),bld(rs[p],mid+1,r,c[p],y);
	up(p);
}
void dfs(int p) {
	if(ls[p]) dfs(ls[p]);
	b[++tot]=p;
	if(rs[p]) dfs(rs[p]);	
}
void rebld(int &p,db l,db r) {
	tot=0; dfs(p);
	bld(p,1,tot,l,r);
}

inline bool bad(int p) {
	return max(sz[ls[p]],sz[rs[p]])>=aph*sz[p];
}
void ins(int &p,db l,db r,A t,int k) {
	if(!p) {
		p=++cnt; c[p]=(l+r)/2,val[p]=t,sz[p]=1; 
		a[k]=p;
		return;
	}
	if(t==val[p]) {
		a[k]=p; return;
	}
	db mid=(l+r)/2;
	if(t<val[p]) ins(ls[p],l,mid,t,k);
		else ins(rs[p],mid,r,t,k);
	up(p);
	if(bad(p)) rebld(p,l,r);
}
namespace ST{
	int s[N];
	inline void up(int p) {
		s[p]=s[p<<1];
		if(c[a[s[p<<1|1]]]>c[a[s[p]]]) s[p]=s[p<<1|1];
	}
	void bld(int p,int l,int r) {
		s[p]=l;
		if(l==r) return;
		int mid=l+r>>1;
		bld(p<<1,l,mid),bld(p<<1|1,mid+1,r);
	}
	void upd(int p,int l,int r,int k) {
		if(l==r) return;
		int mid=l+r>>1;
		if(k<=mid) upd(p<<1,l,mid,k);
			else upd(p<<1|1,mid+1,r,k);
		up(p);
	}
	int ask(int p,int l,int r,int x,int y) {
		if(l==x&&r==y) return s[p];
		int mid=l+r>>1;
		if(y<=mid) return ask(p<<1,l,mid,x,y);
		if(x>mid) return ask(p<<1|1,mid+1,r,x,y);
		int t1=ask(p<<1,l,mid,x,mid),t2=ask(p<<1|1,mid+1,r,mid+1,y);
		if(c[a[t1]]>=c[a[t2]]) return t1;
		return t2;
	}
 }
int main(){
	int T; scanf("%d%d",&n,&T);
	ST::bld(1,1,n);
	while(T--) {
		int u,v;
		scanf("%s%d%d",op,&u,&v);
		if(op[0]=='C') {
			int k; scanf("%d",&k);
			ins(rt,1,INF,(A){a[u],a[v]},k);
			ST::upd(1,1,n,k);
		} else {
			printf("%d\n",ST::ask(1,1,n,u,v));
		}
	}
	return 0;
}
posted @ 2021-03-21 16:16  wwwsfff  阅读(32)  评论(0编辑  收藏  举报