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


为什么我没有copy题面呢?看到题面你就懂了....hh


题意:

定义一个二元组(二元组的两个元素可以是二元组)

如(x,y),其中x可以是(a,(b,c))之类的

定义二元组的比较方式:先比较左边,左边相同再比较右边。

递归比较可以用随便哪颗平衡树维护,70分


考虑对于每个 二元组,对他定义一个实数的映射来表示它的大小,想象我们在将它插入平衡树时,一路比较,按照比较结果判断是插入左子树还是右子树,那实际上它最终的位置就是他的映射,70分做法即为如此,缺陷在于比较是log的,可是,我们既然已经知道他所在最终位置(映射出的实数),且映射的大小关系等价于它本身的大小关系,那为何不用映射出的实数去构造新的二元组呢?这样比较就变成O(1)的了。

可是,映射显然只是一种相对的位置关系,常用的平衡树均有旋转操作,那就意味着映射大小改变,将不断的重新计算映射,得不偿失。可是,非旋转treap和替罪羊树并无旋转操作啊!


代码:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<climits>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define N 500020
using namespace std;
inline int read()
{
    int x=0,f=1;char ch;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
double a[N];
int ts[N],tot;
int R,rt,pos[N],mx[N];
struct data
{
    int l,r;
    friend bool operator < (data x,data y)
    {
	if(a[x.l]<a[y.l])return 1;
	if(a[x.l]==a[y.l]&&a[x.r]<a[y.r])return 1;
	return 0;
    }	
    friend bool operator == (data x,data y)
    {
	if(a[x.l]!=a[y.l]||a[x.r]!=a[y.r])return 0;
	return 1;
    }
};
struct sctree
{
    int cnt,size[N],ls[N],rs[N];
    data v[N];
    void dfs(int k)
    {
		if(!k)return ;
		dfs(ls[k]);
		ts[++tot]=k;
		dfs(rs[k]);
    }
    void build(int &u,int l,int r,double lv,double rv)
    {
		if(l>r){u=0;return;}
		double mv=(lv+rv)/2.0;
		int mid=(l+r)>>1;
		u=ts[mid];a[u]=mv;
		build(ls[u],l,mid-1,lv,mv);
		build(rs[u],mid+1,r,mv,rv);
		size[u]=size[ls[u]]+size[rs[u]]+1;
    }
    void rebuild(int &u,double lv,double rv)
    {
		tot=0;dfs(u);
		build(u,1,tot,lv,rv);
    }
    int insert(int &u,double lv,double rv,data val)
    {
		double mv=(lv+rv)/2.0;
		if(!u)
		{
		    u=++cnt;v[u]=val;
		    a[u]=mv;size[u]=1;
		    return u;
		}
		int p;
		if(val==v[u])return u;
		else
		{
		    size[u]++;
		    if(val<v[u])p=insert(ls[u],lv,mv,val);
		    else p=insert(rs[u],mv,rv,val);
		}
		if(size[u]*0.75>max(size[ls[u]],size[rs[u]]))
		{
		    if(R)
		    {
				if(ls[u]==R)rebuild(ls[u],lv,mv);
				else rebuild(rs[u],mv,rv);
				R=0;
		    }
		}
		else R=u;
		return p;
    }
}sc;
void add(int u,int l,int r,int v)
{
    if(l==r){mx[u]=v;return;}
    int mid=(l+r)>>1;
    if(v<=mid)add(u<<1,l,mid,v);
    else add(u<<1|1,mid+1,r,v);
    int x=mx[u<<1],y=mx[u<<1|1];
    if(a[pos[x]]<a[pos[y]])mx[u]=y;
    else mx[u]=x;
}

int query(int u,int l,int r,int x,int y)
{
    if(x==l&&y==r)return mx[u];
    int mid=(l+r)>>1;int ret=0;
    if(y<=mid)return query(u<<1,l,mid,x,y);
    else if(x>mid)return query(u<<1|1,mid+1,r,x,y);
    else
    {
		int s=query(u<<1,l,mid,x,mid);
		int t=query(u<<1|1,mid+1,r,mid+1,y);
		if(a[pos[s]]>a[pos[ret]])ret=s;
		if(a[pos[t]]>a[pos[ret]])ret=t;
    }return ret;
}
int main()
{
    n=read(),m=read();char ch[10];
    a[0]=-1;sc.insert(rt,0,1,(data){0,0});
    for(int i=1;i<=n;i++)pos[i]=1;
    for(int i=1;i<=n;i++)add(1,1,n,i);
    for(int i=1;i<=m;i++)
    {
		scanf("%s",ch);
		int l=read(),r=read();
		if(ch[0]=='C')
		{
	    	int k=read();
	    	pos[k]=sc.insert(rt,0,1,(data){pos[l],pos[r]});
	    	if(R)sc.rebuild(rt,0,1);R=0;
	    	add(1,1,n,k);
		}
		else printf("%d\n",query(1,1,n,l,r));
    }
}



























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值