BZOJ 3600 没有人的算术

线段树+替罪羊树

黄学长博客里有VFK大爷的题解

题意即给出一类嵌套结构的类似pair的“数”,定义它们之间的大小比较为递归比较first和second。(设first和second分别为数的括号里的左右两边)

查区间max,可以考虑线段树,那么重点就是如何快速判断两个数的大小。

暴力比较可以有30分。

注意到后面有两个点的出现数字个数不超过1000,暗示我们可以直接处理出所有数对的大小关系,可以有50分。

然而后面的测试点出现的数字个数有500000个,怎么办?注意到50分的做法实际上在做一件事情:支持任意两个数比较大小。那为什么不直接把这些数字拿来排序呢? 用平衡树即可。有70分。

用平衡树维护时,比较大小需要去找first和second的排名,是logn的。能不能做到像实数那样有一个确切值可以直接比较?我们需要一个从这种“数”到实数的映射关系,使得它们的大小关系能一一对应。用到一个trick。一个平衡树上结点对应区间(l,r),则设它的值为(l+r)/2,类似于中序遍历的感觉,即可O(1)比较大小。还有一个问题,传统平衡树一般需要旋转,旋转之后子树内的值都要重新算,会T。用 重量平衡树即可。替罪羊树修改均摊O(logn),所以均摊下来修改不会超过O(nlogn)个点。

#include<cstdio>
#define N 500005
using namespace std;
namespace runzhe2000
{
    int in()
    {
        int r = 0;
        char c = getchar();
        while(c<'0' || c>'9')c=getchar();
        while(c>='0' && c<='9')r=r*10+c-'0', c=getchar();
        return r;
    }
    int buffcnt, n, m;
    struct node *null, *tot, *root, *buff[N], *bad, *badf, *ins, *pos[N];
    double badl, badr, alpha = 0.7;
    int lim[N];
    struct node
    {
        double val;
        int siz;
        node *ch[2], *fir, *sec;
        bool smaller(node *x, node *y){return x->fir->val < y->fir->val || (y->fir == x->fir && x->sec->val < y->sec->val);}
        bool equ(node *x, node *y){return y->fir == x->fir && y->sec == x->sec;}
        node *insert(double lv, double rv);
        void recycle()
        {
            if(this == null)return;
            ch[0]->recycle();
            buff[++buffcnt] = this;
            ch[1]->recycle();
        }
        node *rebuild(int l, int r, double lv, double rv)
        {
            if(l > r)return null;
            int mid = (l+r)>>1;
            node *o = buff[mid];
            o->val = (lv+rv)/2;
            o->ch[0] = rebuild(l,mid-1,lv,o->val);
            o->ch[1] = rebuild(mid+1,r,o->val,rv);
            o->siz = r-l+1;
            return o;
        }
    }me[N*2];
    node *newnode(double lv, double rv)
    {
        node *o = ++tot;
        o->ch[0] = o->ch[1] = null;
        o->siz = 1;
        o->val = (lv+rv)/2;
        return o;
    } 
    node* node::insert(double lv, double rv)
    {
        if(this == null){ins->val = (lv+rv)/2; return ins;} 
        if(equ(this,ins))return ins = this;
        node *o = smaller(ins, this) ? (ch[0]=ch[0]->insert(lv,val)): ( ch[1]=ch[1]->insert(val,rv));
        siz = ch[0]->siz + ch[1]->siz + 1;
        if(o->siz > alpha * siz)bad = this, badl = lv, badr = rv;
        if(ch[0] == bad || ch[1] == bad)badf = this;
        return this;
    }
    void init()
    {
        tot = me;
        null = ++tot;
        null->ch[0] = null->ch[1] = null;
        null->siz = null->val = 0;
        root = newnode(0, 1);
        root->fir = newnode(-1,-1);
        root->sec = newnode(-1,-1);
        for(int i = 1; i <= n; i++)pos[i] = root;
    }

    struct seg{int l, r, v;}t[N*5];
    void pushup(int x)
    {
        int lson = x<<1, rson = x<<1|1;
        t[x].v = t[ ( pos[t[lson].v]->val >= pos[t[rson].v]->val ? lson : rson ) ].v;
    }
    void build(int x, int l, int r)
    {
        t[x].l = l, t[x].r = r;
        if(l == r){t[x].v = l; return;}
        int mid = (l+r)>>1;
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
        pushup(x);
    } 
    int query(int x, int l, int r)
    {
        if(l <= t[x].l && t[x].r <= r)return t[x].v;
        int mid = (t[x].l + t[x].r) >> 1, q1=0, q2=0;
        if(l <= mid) q1 = query(x<<1,l,r);
        if(mid < r)q2 = query(x<<1|1,l,r);
        if(!q1 || !q2)return q1?q1:q2;
        return pos[q1]->val >= pos[q2]->val ? q1 : q2;
    }
    void modi(int x, int p)
    {
        if(t[x].l==t[x].r)return;
        int mid = (t[x].l + t[x].r)>>1;
        if(p<=mid)modi(x<<1,p);
        else modi(x<<1|1,p);
        pushup(x);
    }

    char op[3];
    void main()
    {
        n=in(), m=in();
        init();
        build(1,1,n);
        for(int i = 1, l, r, k; i <= m; i++)
        {
            scanf("%s",op);
            if(op[0] == 'C')
            {
                l=in(), r=in(), k=in();

                ins = newnode(0, 1);
                ins->fir = pos[l];
                ins->sec = pos[r];
                ins->siz = 1;                
                bad = null;         

                root->insert(0, 1);
                pos[k] = ins;
                if(bad != null)
                {
                    buffcnt = 0;
                    bad -> recycle();
                    node *tmp = bad -> rebuild(1, buffcnt, badl, badr);
                    if(root == bad)root = tmp;
                    else badf->ch[badf->ch[0] == bad?0:1] = tmp;
                }

                modi(1, k);
            }
            else
            { 
                l=in(), r=in();
                printf("%d\n",query(1,l,r));
            }
        }
    }
}
int main()
{
    runzhe2000::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值