bzoj3600 没有人的算术 替罪羊树+线段树

90 篇文章 0 订阅
3 篇文章 0 订阅

Description


好长啊
这里写图片描述
这里写图片描述

Solution


区间修改+区间查询最大值,考虑用线段树做
注意到我们需要在短时间内求出每个位置上数的rank,如果我们把这些数字插入平衡树中rank就非常好求了
考虑用替罪羊树。我们每次插入时判断一个节点是否不平衡(存在儿子的size>自己的size*α)
如果不平衡就把不平衡节点中序遍历求出来建一棵新的树
这题并没有删除操作,因此我并不需要写我不会的辣鸡回收

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fi first
#define se second

typedef long long LL;

const double alpha=0.75;
const LL INF=4611686018427387903LL;
const int N=400005;

LL val[N];

int pos[N],stack[N],top;

struct data {
    int x,y;

    bool operator < (data b) {
        data a=*this;
        return (val[a.x]!=val[b.x])?(val[a.x]<val[b.x]):(val[a.y]<val[b.y]);
    }

    bool operator == (data b) {
        data a=*this;
        return a.x==b.x&&a.y==b.y;
    }
} ;

struct ScapeTree {
    struct reeNode {
        int l,r,fa,size;
        LL L,R;
    } t[N];

    data p[N];
    int tot,root,rec;

    void dfs(int now) {
        if (t[now].l) dfs(t[now].l);
        stack[++top]=now;
        if (t[now].r) dfs(t[now].r);
    }

    void build(int &now,int l,int r,LL L,LL R) {
        if (r<l) {now=0; return ;}
        int mid=(l+r)>>1;
        LL mi=(L+R)/2;
        now=stack[mid]; val[now]=mi; t[now].L=L; t[now].R=R;
        build(t[now].l,l,mid-1,L,mi); if (t[now].l) t[t[now].l].fa=now;
        build(t[now].r,mid+1,r,mi,R); if (t[now].r) t[t[now].r].fa=now;
        t[now].size=t[t[now].l].size+t[t[now].r].size+1;
    }

    void rebuild(int now,LL L,LL R) {
        int fa=t[now].fa; int k=t[fa].r==now;
        top=0; dfs(now);
        build(now,1,top,L,R);
        t[now].fa=fa;
        if (!fa) {
            root=now;
            return ;
        }
        if (k==1) t[fa].r=now;
        else t[fa].l=now;
    }

    int ins(int &now,LL L,LL R,data u) {
        if (!now) {
            t[now=++tot]=(reeNode) {0,0,0,1,L,R};
            p[now]=u; val[now]=(L+R)/2;
            return now;
        }
        if (p[now]==u) return now;
        LL mi=(L+R)/2;
        int ret;
        if (u<p[now]) {
            ret=ins(t[now].l,L,mi,u);
            t[t[now].l].fa=now;
        } else {
            ret=ins(t[now].r,mi,R,u);
            t[t[now].r].fa=now;
        }
        t[now].size=t[t[now].l].size+t[t[now].r].size+1;
        if (std:: max(t[t[now].l].size,t[t[now].r].size)>=t[now].size*alpha) rec=now;
        return ret;
    }
} sc;

namespace SegTree {
    int max[N];

    int query(int now,int tl,int tr,int l,int r) {
        if (r<l) return 0;
        if (tl==l&&tr==r) return max[now];
        int mid=(tl+tr)>>1;
        if (r<=mid) return query(now<<1,tl,mid,l,r);
        if (l>mid) return query(now<<1|1,mid+1,tr,l,r);
        int qx=query(now<<1,tl,mid,l,mid);
        int qy=query(now<<1|1,mid+1,tr,mid+1,r);
        if (val[pos[qx]]>=val[pos[qy]]) return qx;
        return qy;
    }

    void modify(int now,int tl,int tr,int x) {
        if (tl==tr) return (void) (max[now]=tl);
        int mid=(tl+tr)>>1;
        if (x<=mid) modify(now<<1,tl,mid,x);
        else modify(now<<1|1,mid+1,tr,x);
        int qx=max[now<<1],qy=max[now<<1|1];
        max[now]=qx;
        if (val[pos[qx]]<val[pos[qy]]) max[now]=qy;
    }

}

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

int main(void) {
    freopen("data.in","r",stdin);
    freopen("myp.out","w",stdout);
    int n=read(),m=read(); val[0]=-INF-1;
    sc.ins(sc.root,-INF,INF,(data) {0,0});
    rep(i,1,n) pos[i]=1,SegTree:: modify(1,1,n,i);
    for (;m--;) {
        char ch=getchar(); for (;ch!='Q'&&ch!='C';ch=getchar());
        int l=read(),r=read();
        if (ch=='C') {
            int k=read();
            pos[k]=sc.ins(sc.root,-INF,INF,(data){pos[l],pos[r]});
            SegTree:: modify(1,1,n,k);
            if (sc.rec) {
                sc.rebuild(sc.rec,sc.t[sc.rec].L,sc.t[sc.rec].R);
                sc.rec=0;
            }
        } else printf("%d\n", SegTree:: query(1,1,n,l,r));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值