bzoj 3065: 带插入区间K小值 替罪羊树套线段树

题意

给出一个序列,要求资瓷以下操作:
在某个位置前插入一个数
修改某个位置的数
询问区间第k小
强制在线
原序列长度 <= 35000
插入个数 <= 35000,修改个数 <= 70000,查询个数 <= 70000 ,0 <= 每时每刻的权值 <= 70000

分析

由于强制在线且要插入,所以外层显然不能用线段树之类的静态数据结构,那么就考虑用平衡树。内层的话由于是找第k小,所以选用了线段树。
由于带旋转的平衡树每次旋转后要花费大量时间来重构线段树,所以不是很可行。我们便可以用替罪羊树。
一开始想打函数式线段树,但发现不会打垃圾回收,于是就去打了普通的线段树。区别就在于函数式线段树在暴力重构的时候是把两棵儿子的函数式线段树合并上来,然后把自己的权值加入;普通线段树就是每次把所有子节点的权值暴力加入。
不过据说时间不会相差太远。
询问的话可以把需要的节点全部拿下来然后一起跑二分。
注意要垃圾回收。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

const int N=70005;

int n,m,num,root[N],top,stack[N],rt,R;
struct tree{int l,r,s,fa,val;}t[N];
vector<int> p,q;

int read()
{
    int x=0,f=1;char ch=getchar();
    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;
}

struct Segtree
{
    int sz;
    struct tree{int l,r,s;}t[20000005];
    vector<int> re;

    int newnode()
    {
        if (!re.size()) return ++sz;
        else {int x=re.back();re.pop_back();return x;}
    }

    void reuse(int &d)
    {
        if (!d) return;
        reuse(t[d].l);reuse(t[d].r);
        re.push_back(d);t[d].s=0;d=0;
    }

    void ins(int &d,int l,int r,int x,int y)
    {
        if (!d) d=newnode();t[d].s+=y;
        if (l==r) {if (!t[d].s) reuse(d);return;}
        int mid=(l+r)/2;
        if (x<=mid) ins(t[d].l,l,mid,x,y);
        else ins(t[d].r,mid+1,r,x,y);
        if (!t[d].s) reuse(d);
    }
}seg;

void dfs(int d)
{
    seg.reuse(root[d]);
    if (t[d].l) dfs(t[d].l);
    stack[++top]=d;
    if (t[d].r) dfs(t[d].r);
}

void build(int &d,int l,int r)
{
    if (l>r) {d=0;return;}
    int mid=(l+r)/2;d=stack[mid];
    build(t[d].l,l,mid-1);if (t[d].l) t[t[d].l].fa=d;
    build(t[d].r,mid+1,r);if (t[d].r) t[t[d].r].fa=d;
    t[d].s=t[t[d].l].s+t[t[d].r].s+1;
    for (int i=l;i<=r;i++) seg.ins(root[d],0,70000,t[stack[i]].val,1);
}

void rebuild(int d)
{
    top=0;dfs(d);
    int tmp=t[d].fa,op=(d==t[tmp].l?0:1);
    build(d,1,top);
    if (!tmp) {t[d].fa=0,rt=d;return;}
    t[d].fa=tmp;
    if (!op) t[tmp].l=d;
    else t[tmp].r=d;
}

void ins(int &d,int x,int y)
{
    if (!d) {d=++num;seg.ins(root[d],0,70000,y,1);t[d].val=y;t[d].s=1;return;}
    seg.ins(root[d],0,70000,y,1);t[d].s++;
    if (t[t[d].l].s+1>=x) ins(t[d].l,x,y),t[t[d].l].fa=d;
    else ins(t[d].r,x-t[t[d].l].s-1,y),t[t[d].r].fa=d;
    if (t[d].s*0.75<max(t[t[d].l].s,t[t[d].r].s)) R=d;
}

int modify(int d,int x,int y)
{
    seg.ins(root[d],0,70000,y,1);
    if (t[t[d].l].s+1==x)
    {
        int w=t[d].val;t[d].val=y;
        seg.ins(root[d],0,70000,w,-1);
        return w;
    }
    int w;
    if (t[t[d].l].s>=x) w=modify(t[d].l,x,y);
    else w=modify(t[d].r,x-t[t[d].l].s-1,y);
    seg.ins(root[d],0,70000,w,-1);
    return w;
}

void get(int d,int l,int r)
{
    if (l>r) return;
    if (l==1&&r==t[d].s) {p.push_back(root[d]);return;}
    int x=t[t[d].l].s,y=t[d].s;
    if (l<=x+1&&r>=x+1) q.push_back(d);
    get(t[d].l,l,min(x,r));get(t[d].r,max(1,l-x-1),r-x-1);
}

int query(int x,int y,int k)
{
    p.clear();q.clear();
    get(rt,x,y);
    int l=0,r=70000;
    while (l<r)
    {
        int s=0,mid=(l+r)/2;
        for (int i=0;i<p.size();i++) s+=seg.t[seg.t[p[i]].l].s;
        for (int i=0;i<q.size();i++) s+=(t[q[i]].val<=mid&&t[q[i]].val>=l);
        if (s>=k)
        {
            r=mid;
            for (int i=0;i<p.size();i++) p[i]=seg.t[p[i]].l;
        }
        else
        {
            l=mid+1;k-=s;
            for (int i=0;i<p.size();i++) p[i]=seg.t[p[i]].r;
        }
    }
    return l;
}

int main()
{
    n=read();
    for (int i=1;i<=n;i++)
    {
        int x=read();
        ins(rt,i,x);
        if (R) rebuild(R),R=0;
    }
    m=read();int ans=0;
    while (m--)
    {
        char ch[2];
        scanf("%s",ch);int l=read()^ans,r=read()^ans;
        if (ch[0]=='Q')
        {
            int k=read()^ans;
            printf("%d\n",ans=query(l,r,k));
        }
        else if (ch[0]=='M') modify(rt,l,r);
        else
        {
            ins(rt,l,r);
            if (R) rebuild(R),R=0;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值