洛谷 P3380 bzoj3196 Tyvj1730 【模板】二逼平衡树(树套树)

【模板】二逼平衡树(树套树)

题目描述

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

查询k在区间内的排名

查询区间内排名为k的值

修改某一位值上的数值

查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)

查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)

注意上面两条要求和tyvj或者bzoj不一样,请注意

输入输出格式

输入格式:

第一行两个数 n,m 表示长度为n的有序序列和m个操作

第二行有n个数,表示有序序列

下面有m行,opt表示操作标号

若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名

若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数

若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k

若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱

若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继

输出格式:

对于操作1,2,4,5各输出一行,表示查询结果

说明

时空限制:2s,1256M

\(n,m \leq 5\cdot {10}^4\)

保证有序序列所有值在任何时刻满足\([0, {10} ^8]\)

题目来源:bzoj3196 / Tyvj1730 二逼平衡树,在此鸣谢

此数据为洛谷原创。(特别提醒:此数据不保证操作4、5一定存在,故请务必考虑不存在的情况)


前几天写了个树状数组套平衡树,最后懒得调了

花了很久写+调弄了树状数组套可持久化线段树

发现有思想的是 在多个可持久化树上一起二分

很烦人的是查排名时的一些存在性问题

最关键的是权值线段树想离散开点还必须离线

代码也不好看,以后还会写的


Code

#include <cstdio>
#include <algorithm>
#define sf num[i].second.first
#define ss num[i].second.second
using namespace std;
const int N=300010;
const int inf=2147483647;
int sum[N*50],ls[N*50],rs[N*50],root[N],las[N],tot,n,m,is,n_,n0,dat[N<<1];
pair <int,pair<int,int> > num[N<<1];
struct node
{
    int opt,l,r,k;
}op[N];
void change(int &now,int l,int r,int pos,int delta)
{
    if(!now) now=++tot;
    sum[now]+=delta;
    if(l==r) return;
    int mid=l+r>>1;
    if(pos<=mid) change(ls[now],l,mid,pos,delta);
    else change(rs[now],mid+1,r,pos,delta);
}
int query(int now,int l,int r,int pos)//1-pos的值
{
    if(l==r)
        return is=sum[now],0;
    if(!now||!pos) return sum[now];
    int mid=l+r>>1;
    if(pos<=mid) return query(ls[now],l,mid,pos);
    else return sum[ls[now]]+query(rs[now],mid+1,r,pos);
}
void add(int x,int pos,int delta)
{
    while(x<=n_)
    {
        change(root[x],1,n,pos,delta);
        x+=x&-x;
    }
}
int Rank(int l,int r,int pos)//查询l-r区间pos的排名
{
    if(!pos) return 0;
    int rk=0,cnt0=0;
    for(int i=l-1;i;i-=i&-i)
        is=0,rk-=query(root[i],1,n,pos),cnt0-=is;
    for(int i=r;i;i-=i&-i)
        is=0,rk+=query(root[i],1,n,pos),cnt0+=is;
    return rk+(cnt0>0);
}
int rl[N],rr[N];
int frank(int l,int r,int k)//l到r排名为k的数
{
    int ad,de;
    for(int i=l-1;i;i-=i&-i) rl[i]=root[i];
    for(int i=r;i;i-=i&-i) rr[i]=root[i];
    int L=1,R=n;
    while(L<R)
    {
        ad=de=0;
        int Mid=L+R>>1;
        for(int i=l-1;i;i-=i&-i) de+=sum[ls[rl[i]]];
        for(int i=r;i;i-=i&-i) ad+=sum[ls[rr[i]]];
        if(ad-de>=k)
        {
            R=Mid;
            for(int i=l-1;i;i-=i&-i) rl[i]=ls[rl[i]];
            for(int i=r;i;i-=i&-i) rr[i]=ls[rr[i]];
        }
        else
        {
            L=Mid+1;
            k-=ad-de;
            for(int i=l-1;i;i-=i&-i) rl[i]=rs[rl[i]];
            for(int i=r;i;i-=i&-i) rr[i]=rs[rr[i]];
        }
    }
    return dat[L];
}
void pre(int l,int r,int pos)
{
    int rk=Rank(l,r,pos-1);
    if(!rk) printf("%d\n",-inf);
    else printf("%d\n",frank(l,r,rk));
}
void suc(int l,int r,int pos)
{
    add(l,pos+1,1);
    int rk=Rank(l,r,pos+1);
    add(l,pos+1,-1);
    if(rk==r+2-l) printf("%d\n",inf);
    else printf("%d\n",frank(l,r,rk));
}
void init()
{
    scanf("%d%d",&n_,&m);
    for(int i=1;i<=n_;i++)
    {
        scanf("%d",&num[i].first);
        num[i].second.first=i;
    }
    n0=n_;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&op[i].opt);
        if(op[i].opt==3)
            scanf("%d%d",&op[i].l,&op[i].k);
        else
            scanf("%d%d%d",&op[i].l,&op[i].r,&op[i].k);
        if(op[i].opt!=2)
        {
            num[++n0].first=op[i].k;
            num[n0].second.first=i+n_;
        }
    }
    sort(num+1,num+1+n0);
    num[0].first=inf;
    for(int i=1;i<=n0;i++)
    {
        if(num[i].first!=num[i-1].first) ++n;
        ss=n,dat[n]=num[i].first;
    }
    for(int i=1;i<=n0;i++)
    {
        if(sf<=n_) las[sf]=ss,add(sf,ss,1);
        else op[sf-n_].k=ss;
    }
}
void work()
{
    for(int i=1;i<=m;i++)
    {
        if(op[i].opt==1)
        {
            add(op[i].l,op[i].k,1);
            printf("%d\n",Rank(op[i].l,op[i].r,op[i].k));
            add(op[i].l,op[i].k,-1);
        }
        else if(op[i].opt==2) printf("%d\n",frank(op[i].l,op[i].r,op[i].k));
        else if(op[i].opt==3)
        {
            add(op[i].l,las[op[i].l],-1);
            las[op[i].l]=op[i].k;
            add(op[i].l,las[op[i].l],1);
        }
        else if(op[i].opt==4)
            pre(op[i].l,op[i].r,op[i].k);
        else
            suc(op[i].l,op[i].r,op[i].k);
    }
}
int main()
{
    init();
    work();
    return 0;
}

2018.8.1

转载于:https://www.cnblogs.com/butterflydew/p/9403390.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值