[题解]bzoj3600 没有人的算数

29 篇文章 0 订阅
11 篇文章 0 订阅



Solution

就是线段树,最麻烦的地方就是比较两个数的大小。
我们可以想到给每个数映射到一个double值,但是double的精度有限,如果数太多就会挂掉。所以我们可以用平衡树维护。但是插入的时候还是有比较大小的问题。
注意到新数由原来的数生成,所以原来的数在平衡树中的大小可以用来进行比较。我们可以把每一个平衡树中的节点对应区间(l,r),然后他的左孩子对应(l,mid)
,右孩子对应(mid,r),每个节点的权值为mid,这样就在精度有限的情况下(因为平衡树不会太高),把数对映射到了平衡树中。为了维护每个节点的l、r,我们不能用带旋转的平衡树,所以考虑用替罪羊或者Treap。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=500010;
int n,m,mx[maxn<<2],pos[maxn];
double val[maxn];

template<typename T>inline void read(T &x){
    T f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(x=0;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    x*=f;
}

struct Data{
    int fir,sec;
    bool operator<(Data b){
        if(val[fir]==val[b.fir])return val[sec]<val[b.sec];
        return val[fir]<val[b.fir];
    }
    bool operator==(Data b){
        if(val[fir]!=val[b.fir]||val[sec]!=val[b.sec])return false;
        return true;
    }
};
struct ScapeGoat{
    int id,A[maxn<<1],top,sum,root;
    double idll,idrr;
    int size[maxn<<1],ch[maxn<<1][2],fa[maxn<<1];
    Data a[maxn<<1];
    ScapeGoat(){
        id=top=sum=root=0;
        memset(size,0,sizeof size);
        memset(ch,0,sizeof ch);
        memset(a,0,sizeof a);
        memset(fa,0,sizeof fa);
    }
    bool Bad(int x){
        return max(size[ch[x][0]],size[ch[x][1]])>0.75*size[x]+4;
    }
    int Build(int f,int l,int r,double ll,double rr){
        if(l>r)return 0;
        int mid=(l+r)>>1,x=A[mid];
        double mv=(ll+rr)/2.0;
        fa[x]=f;val[x]=mv;
        ch[x][0]=Build(x,l,mid-1,ll,mv);
        ch[x][1]=Build(x,mid+1,r,mv,rr);
        size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
        return x;
    }
    void Dfs(int x){
        if(!x)return;
        Dfs(ch[x][0]);ch[x][0]=0;
        A[++top]=x;
        Dfs(ch[x][1]);ch[x][1]=0;
    }
    void ReBuild(int x,double ll,double rr){
        top=0;Dfs(x);
        int p=(x==ch[fa[x]][1]);
        if(fa[x])ch[fa[x]][p]=Build(fa[x],1,top,ll,rr);
        else root=Build(0,1,top,ll,rr);
    }
    int Insert(int &x,int f,double ll,double rr,Data v){
        double mv=(ll+rr)/2.0;
        if(!x){
            val[x=++sum]=mv;a[x]=v;
            size[x]=1;fa[x]=f;
            return x;
        }
        int p;
        if(v==a[x])return x;
        else{
            size[x]++;
            if(a[x]<v)p=Insert(ch[x][1],x,mv,rr,v);
            else p=Insert(ch[x][0],x,ll,mv,v);
        }
        if(Bad(x))id=x,idll=ll,idrr=rr;
        return p;
    }
}sg;

void Modify(int x,int l,int r,int p){
    if(l==r)return mx[x]=l,void();
    int mid=(l+r)>>1;
    if(p<=mid)Modify(x<<1,l,mid,p);
    else Modify(x<<1|1,mid+1,r,p);
    int a=mx[x<<1],b=mx[x<<1|1];
    if(val[pos[a]]>=val[pos[b]])mx[x]=a;
    else mx[x]=b;
}
int Query(int x,int l,int r,int ql,int qr){
    if(l>=ql&&r<=qr)return mx[x];
    int mid=(l+r)>>1;
    int p=0,ans=0;
    if(ql<=mid)ans=Query(x<<1,l,mid,ql,qr);
    if(qr>mid){
        p=Query(x<<1|1,mid+1,r,ql,qr);
        if(!ans)ans=p;
        else if(val[pos[ans]]<val[pos[p]])ans=p;
    }
    return ans;
}

int main(){
    read(n);read(m);
    sg.Insert(sg.root,0,0,1,Data{0,0});
    for(int i=1;i<=n;i++)pos[i]=1;
    for(int i=1;i<=n;i++)Modify(1,1,n,i);
    while(m--){
        char opt[10];
        int l,r,k;
        scanf("%s",opt);read(l);read(r);
        if(opt[0]=='C'){
            read(k);sg.id=0;
            pos[k]=sg.Insert(sg.root,0,0,1,Data{pos[l],pos[r]});
            if(sg.id)sg.ReBuild(sg.id,sg.idll,sg.idrr);
            Modify(1,1,n,k);
        }
        else printf("%d\n",Query(1,1,n,l,r));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值