bzoj 3600: 没有人的算术 替罪羊树

       窝终于能自己手打替罪羊树辣~~~!(←妈的智障,还不是抄黄学长的)

       在cls的那篇论文里面讲到了用替罪羊树实现O(logN)修改,O(1)询问两个节点的前后关系,这道题目就是要用到这个。

       首先是不是感觉这个括号序列很高大上呢~~?(づ ̄ 3 ̄)づ实际上,我们可以用三个排名来表示一个括号序列,分别为l,r,v,其中v表示的就是这个括号序列在当前所有已经出现过的括号序列中的排名,同时这个括号序列也等价于(l,r)。这样在修改的时候,新的括号序列为(v1,v2),然后找到它的排名作为这个括号序列的v即可。那么如果用普通的平衡树来维护所有括号序列的名次树的话查询就是O(logN)了,那么显然这道题目我们需要在外面套一颗线段树,那么就变成O(log^2N)了。

       然后只要把普通的平衡树改成替罪羊树维护一下tag就变成O(1)辣~\(≧▽≦)/~

      另外替罪羊树真的好短只有30行~~~实测平衡因子取0.77左右比较快~~~~~

AC代码如下:

#include<iostream>
#include<cstdio>
#define ll long long
#define N 500005
using namespace std;

int n,m,tsh_cnt,pos[N],val[N],tsh[N]; bool flag;
struct node{ int l,r; }; ll id[N],inf=(ll)4611686018427387904;
bool operator <(node x,node y){
	return id[x.l]<id[y.l] || id[x.l]==id[y.l] && id[x.r]<id[y.r];
}
bool operator ==(node x,node y){
	return id[x.l]==id[y.l] && id[x.r]==id[y.r];
}
struct scp_node{
	int trtot,rt,ls[N],rs[N],sz[N]; node nd[N];
	void del(int k){
		if (ls[k]) del(ls[k]); tsh[++tsh_cnt]=k; if (rs[k]) del(rs[k]);
	}
	void build(int &k,int x,int y,ll l,ll r){
		int z=(x+y)>>1; ll mid=(l+r)>>1;
		k=tsh[z]; id[k]=mid; sz[k]=y-x+1;
		if (x<z) build(ls[k],x,z-1,l,mid); else ls[k]=0;
		if (z<y) build(rs[k],z+1,y,mid,r); else rs[k]=0;
	}
	void rebuild(int &k,ll l,ll r){
		tsh_cnt=0; del(k); build(k,1,tsh_cnt,l,r);
	}
	int ins(int &k,ll l,ll r,node x){
		ll mid=(l+r)>>1;
		if (!k){
			k=++trtot; sz[k]=1; id[k]=mid;
			nd[k]=x; return k;
		}
		if (x==nd[k]) return k; else{
			sz[k]++;
			int t=(x<nd[k])?ins(ls[k],l,mid,x):ins(rs[k],mid,r,x);
			if (sz[k]*0.77>max(sz[ls[k]],sz[rs[k]])){
				if (flag)
					if (x<nd[k]) rebuild(ls[k],l,mid); else rebuild(rs[k],mid,r);
				flag=0;
			} else flag=1;
			return t;
		}
	}
}scp;
void build(int k,int l,int r){
	val[k]=l; if (l==r) return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid); build(k<<1|1,mid+1,r);
}
void mdy(int k,int l,int r,int x){
	if (l==r){ val[k]=x; return; }
	int mid=(l+r)>>1;
	if (x<=mid) mdy(k<<1,l,mid,x); else mdy(k<<1|1,mid+1,r,x);
	if (id[pos[val[k<<1]]]<id[pos[val[k<<1|1]]]) val[k]=val[k<<1|1]; else val[k]=val[k<<1]; 
}
int qry(int k,int l,int r,int x,int y){
	if (l==x && r==y) return val[k]; int mid=(l+r)>>1;
	if (y<=mid) return qry(k<<1,l,mid,x,y);
	else if (x>mid) return qry(k<<1|1,mid+1,r,x,y); else{
		int t1=qry(k<<1,l,mid,x,mid),t2=qry(k<<1|1,mid+1,r,mid+1,y);
		if (id[pos[t2]]>id[pos[t1]]) t1=t2; return t1;
	}
}
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
int main(){
	n=read(); m=read(); int i; id[0]=-inf-1;
	scp.ins(scp.rt,-inf,inf,(node){0,0});
	for (i=1; i<=n; i++) pos[i]=1; build(1,1,n);
	char ch; int x,y,z;
	while (m--){
		ch=getchar(); while (ch<'A' || ch>'Z') ch=getchar();
		if (ch=='C'){
			x=read(); y=read(); z=read();
			pos[z]=scp.ins(scp.rt,-inf,inf,(node){pos[x],pos[y]});
			if (flag) scp.rebuild(scp.rt,-inf,inf);
			mdy(1,1,n,z);
		} else{
			x=read(); y=read(); printf("%d\n",qry(1,1,n,x,y));
		}
	}
	return 0;
}


by lych

2016.4.12

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值