bzoj 3600 - 没有人的算术

Description

blablabla
开个脑洞:
题目讲了两页跟圣经有关的东东
替罪羊的名字又来自圣经
所以这题是替罪羊

Analysis

第二问求区间最大值用线段树就可以搞定
难度在第一问
不难想到合并时double乱搞搞出一个代表它的大小作为映射
然后当然会爆精度-.-

Solution

1.不难发现,对于将x,y合并成(x,y)的操作中
我们把题目中(l,r)稍微改成(rk[l],rk[r])
x的排名和y的排名都是已知的,所以能很快的找到(x,y)应该排第几
这就是平衡树了
2.但是,这样排名还是难以控制
我们可以在一个点上加上一个控制区域
root控制区域为(l,r)
lc[root]控制(0,mid-1),rc[root]控制(mid+1,r)
root映射到得相对大小就是mid
3.但是,有几个问题:
①一旋转这些控制区间就乱套了
②深度大了跟暴力double是一个样的
那我们就用替罪羊树的优美性质,保持重量平衡,且重构时才修改映射
4.但是,因为排名会变,把(l,r)转化成(rk[l],rk[r])存储是不现实的,改为存(pos[l],pos[r]),pos[l]指向替罪羊中的一个点,rk[pos[l]]才是排名

Notice

1.替罪羊的 深度,字数大小 都是均摊log的
2.算映射mid时小心l+r爆炸
3.重构时虽然映射变了,但其中的相对大小关系不变,是不用修改线段树的

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef double db;
const int M=500007;
const db alpha=0.75;
const LL INF=9223372036854775807;

inline int rd(){
	int x=0;bool f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-')f=0;
	for(;isdigit(c);c=getchar()) x=x*10+c-48;
	return f?x:-x;
}

int n,m;
char s[7];

int pos[M];//pos[i]表示a[i]对应的treap中节点
LL rk[M];//treap中节点映射得到的一个排名

struct node{
	int x,y;
	node(int xx=0,int yy=0){x=xx;y=yy;}
};
bool operator ==(node x,node y){return rk[x.x]==rk[y.x]&&rk[x.y]==rk[y.y];}
bool operator < (node x,node y){return rk[x.x]<rk[y.x]||(rk[x.x]==rk[y.x]&&rk[x.y]<rk[y.y]);}

struct ScapeGt{
	node val[M];//用于treap比较大小
	int lc[M],rc[M];
	int sz[M];
	int root,tot;
	int *sgt;
	LL ssl,ssr;
	int que[M],tque;

	bool isbad(int x){
		return (db)sz[x]*alpha<(db)max(sz[lc[x]],sz[rc[x]]);
	}

	void pushup(int x){
		sz[x]=1+sz[lc[x]]+sz[rc[x]];
	}

	void dfs(int x){
		if(lc[x]) dfs(lc[x]);
		que[++tque]=x;
		if(rc[x]) dfs(rc[x]);
	}

	int build(int l,int r,LL sl,LL sr){
		if(l>r) return 0;
		int mid=l+r>>1;
		LL smid=sl+(sr-sl)/2;
		int x=que[mid];
		sz[x]=1;
		rk[x]=smid;
		lc[x]=build(l,mid-1,sl,smid-1);
		rc[x]=build(mid+1,r,smid+1,sr);
		pushup(x);
		return x;
	}

	void rebuild(int *x,LL l,LL r){
		tque=0;
		dfs(*x);
		*x=build(1,tque,l,r);
	}

	int insert(int &x,node d,LL l,LL r){
		LL mid=l+(r-l)/2;
		if(!x){
			x=++tot;
			val[x]=d;
			sz[x]=1;
			lc[x]=rc[x]=0;
			rk[x]=mid;
			return x;
		}
		if(val[x]==d) return x;
		int tp;
		if(d<val[x]) tp=insert(lc[x],d,l,mid-1);
		else tp=insert(rc[x],d,mid+1,r);
		pushup(x);
		
		if(isbad(x)) sgt=&x,ssl=l,ssr=r;
		return tp;
	}

	int ins(node d){
		sgt=0;
		int x=insert(root,d,0,INF);//前面写-INF算mid会炸掉 
		if(sgt) rebuild(sgt,ssl,ssr);
		return x;
	}
}SGT;

struct segtree{
	int mx[M];
	int gmx(int x,int y){
		if(rk[pos[x]]==rk[pos[y]]) return x<y?x:y;
		return rk[pos[x]]>rk[pos[y]]?x:y;
	}
	void pushup(int x){
		mx[x]=gmx(mx[x<<1],mx[x<<1|1]);
	}
	void build(int x,int l,int r){
		if(l==r){
			mx[x]=l;
			return;
		}
		int mid=l+r>>1;
		build(x<<1,l,mid);
		build(x<<1|1,mid+1,r);
		pushup(x);
	}
	void mdf(int x,int l,int r,int to){
		if(l==r) return;
		int mid=l+r>>1;
		if(to<=mid) mdf(x<<1,l,mid,to);
		else mdf(x<<1|1,mid+1,r,to);
		pushup(x);
	}
	int get(int x,int l,int r,int tl,int tr){
		if(tl<=l&&r<=tr) return mx[x];
		int mid=l+r>>1;
		if(tl<=mid&&mid<tr)
			return gmx(get(x<<1,l,mid,tl,tr),get(x<<1|1,mid+1,r,tl,tr));
		if(tl<=mid) return get(x<<1,l,mid,tl,tr);
		if(mid<tr) return get(x<<1|1,mid+1,r,tl,tr);
	}
}Seg;

int main(){
	int i,x,y,z;
	n=rd(), m=rd();
	SGT.ins(node(0,0));
	for(i=1;i<=n;i++) pos[i]=1;
	Seg.build(1,1,n);
	for(i=1;i<=m;i++){
		scanf("%s",s);
		if(s[0]=='C'){
			x=rd(),y=rd(),z=rd();
			node nw=node(pos[x],pos[y]);
			pos[z]=SGT.ins(nw);
			Seg.mdf(1,1,n,z);
		}
		else{
			x=rd(),y=rd();
			printf("%d\n",Seg.get(1,1,n,x,y));
		}
	}
	return 0;
}
posted @ 2017-01-17 16:58  _zwl  阅读(443)  评论(0编辑  收藏  举报