BZOJ3196 二逼平衡树(线段树套线段树)

题目大意

一种数据结构,维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)

Solution

查询k在的排名,k的前驱,后继,能马上想到用平衡树维护。而因为是在区间中询问的,所以要用到树套树。外层维护一棵线段树,内层平衡树,就能查询区间了。复杂度 (logn)2
而操作2,好像只能二分答案,再进行操作1,复杂度达到了令人绝望的 (logn)3 ……
其实我们可以将内层的平衡树替换为一棵权值线段树。同样可以实现操作1和操作3,操作2时把这个区间包含的权值线段树都拿出来,再一起二分,复杂度就变成了 (logn)2 。操作4和5有点麻烦,但是前驱可以通过操作1和2得到,后继也同理。我们就能维护所有操作了。总时间复杂度 nlogn2

考虑到一个问题,维护权值线段树内存是 nlogn ,整颗树的内存就变成了 nlogn2 ,而BZOJ上内存只给了128MB,只能开3个10000000的数组,根本过不了TT。
于是只好硬生生把原来在线的问题变成了离线。先进行离散化,把权值线段树值域缩小,才刚好够了空间。空间是不是很绝望?

/**************************************************************
    Problem: 3196
    Language: C++
    Result: Accepted
    Time:4916 ms
    Memory:120984 kb
****************************************************************/

#include<cstdio>
#include<algorithm>
using namespace std;
int cnt,lson[10000010],rson[10000010],tree[10000010];
int root[200010],a[50010],q[10010];
int Small,Max,ta,b[100010],O[50010],l[50010],r[50010],k[50010],val[200010];
void insert(int &u,int l,int r,int key)
{
    if (!u) u=++cnt;
    if (l==r)
    {
        tree[u]++;
        return;
    }
    int mid=(l+r)>>1;
    if (key<=mid) insert(lson[u],l,mid,key);
    else insert(rson[u],mid+1,r,key);
    tree[u]=tree[lson[u]]+tree[rson[u]];
}
void build(int l,int r,int t)
{
    for (int i=l;i<=r;i++)
        insert(root[t],0,100000,a[i]);
    if (l==r)
    { 
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,t<<1);
    build(mid+1,r,t<<1|1);    
}
void query_small(int t,int l,int r,int x,int y)
{
    if (!t) return;
    if (l==x&&y==r)
    {
        Small+=tree[t];
        return;
    }
    int mid=(l+r)>>1;
    if (y<=mid) query_small(lson[t],l,mid,x,y);
    else if (x>mid) query_small(rson[t],mid+1,r,x,y);
    else query_small(lson[t],l,mid,x,mid),query_small(rson[t],mid+1,r,mid+1,y);
}
void query_randmin(int l,int r,int x,int y,int t,int key)
{
    if (l==x&&y==r)
    {
        query_small(root[t],0,100000,0,key-1);
        return;
    }
    int mid=(l+r)>>1;
    if (y<=mid) query_randmin(l,mid,x,y,t<<1,key);
    else if (x>mid) query_randmin(mid+1,r,x,y,t<<1|1,key);
    else query_randmin(l,mid,x,mid,t<<1,key),query_randmin(mid+1,r,mid+1,y,t<<1|1,key); 
}
void query_queue(int l,int r,int x,int y,int t)
{
    if (l==x&&y==r)
    {
        q[++ta]=root[t];
        return;
    }
    int mid=(l+r)>>1;
    if (y<=mid) query_queue(l,mid,x,y,t<<1);
    else if (x>mid) query_queue(mid+1,r,x,y,t<<1|1);
    else query_queue(l,mid,x,mid,t<<1),query_queue(mid+1,r,mid+1,y,t<<1|1); 
}
int query_rand(int l,int r,int k)
{
    if (l==r) return l;
    int mid=(l+r)>>1;
    int sum=0;
    for (int i=1;i<=ta;i++)
        sum+=tree[lson[q[i]]];
    if (k<=sum) 
    {
        for (int i=1;i<=ta;i++) q[i]=lson[q[i]];
        return query_rand(l,mid,k);
    }
    else
    {
        for (int i=1;i<=ta;i++) q[i]=rson[q[i]];
        return query_rand(mid+1,r,k-sum);
    }
}
void query_max(int t,int l,int r,int x,int y)
{
    if (!t) return;
    if (l==x&&y==r)
    {
        Max+=tree[t];
        return;
    }
    int mid=(l+r)>>1;
    if (y<=mid) query_max(lson[t],l,mid,x,y);
    else if (x>mid) query_max(rson[t],mid+1,r,x,y);
    else query_max(lson[t],l,mid,x,mid),query_max(rson[t],mid+1,r,mid+1,y);
}
void query_randmax(int l,int r,int x,int y,int t,int key)
{
    if (l==x&&y==r)
    {
        query_max(root[t],0,100000,key+1,100000);
        return;
    }
    int mid=(l+r)>>1;
    if (y<=mid) query_randmax(l,mid,x,y,t<<1,key);
    else if (x>mid) query_randmax(mid+1,r,x,y,t<<1|1,key);
    else query_randmax(l,mid,x,mid,t<<1,key),query_randmax(mid+1,r,mid+1,y,t<<1|1,key); 
}
int query_rands(int l,int r,int k)
{
    if (l==r) return l;
    int mid=(l+r)>>1;
    int sum=0;
    for (int i=1;i<=ta;i++)
        sum+=tree[rson[q[i]]];
    if (k<=sum) 
    {
        for (int i=1;i<=ta;i++)
            q[i]=rson[q[i]];
        return query_rands(mid+1,r,k);
    }
    else
    {
        for (int i=1;i<=ta;i++)
            q[i]=lson[q[i]];
        return query_rands(l,mid,k-sum);
    }
}
void ins(int &t,int l,int r,int key,int Add)
{
    if (!t) t=++cnt;
    if (l==r)
    {
        tree[t]+=Add;
        return;
    }
    int mid=(l+r)>>1;
    if (key<=mid) ins(lson[t],l,mid,key,Add);
    else ins(rson[t],mid+1,r,key,Add);
    tree[t]=tree[lson[t]]+tree[rson[t]];
}
void modify(int l,int r,int t,int pos,int key,int Add)
{
    if (l==r)
    {
        ins(root[t],0,100000,key,Add);
        return;
    }
    int mid=(l+r)>>1;
    if (pos<=mid) modify(l,mid,t<<1,pos,key,Add);
    else modify(mid+1,r,t<<1|1,pos,key,Add);
    ins(root[t],0,100000,key,Add);
}
int main()
{
    //freopen("data10.in","r",stdin);
    //freopen("data10.out","w",stdout); 
    int n,m,cc=0;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[++cc]=a[i];
    }
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&O[i]);
        if (O[i]==1) scanf("%d%d%d",&l[i],&r[i],&k[i]),b[++cc]=k[i];
        if (O[i]==2) scanf("%d%d%d",&l[i],&r[i],&k[i]);
        if (O[i]==3) scanf("%d%d",&l[i],&k[i]),b[++cc]=k[i];
        if (O[i]==4) scanf("%d%d%d",&l[i],&r[i],&k[i]),b[++cc]=k[i];
        if (O[i]==5) scanf("%d%d%d",&l[i],&r[i],&k[i]),b[++cc]=k[i];
    }
    sort(b+1,b+1+cc);
    int n1=unique(b+1,b+1+cc)-b-1;
    for (int i=1;i<=n;i++)
    {
        int xx=lower_bound(b+1,b+1+n1,a[i])-b;
        val[xx]=a[i],a[i]=xx;
    } 
    for (int i=1;i<=m;i++)
        if (O[i]!=2) 
        {
            int xx=lower_bound(b+1,b+1+n1,k[i])-b;
            val[xx]=k[i],k[i]=xx;
        }
    build(1,n,1);
    for (int i=1;i<=m;i++)
    {
        if (O[i]==1)
        {
            Small=0;
            query_randmin(1,n,l[i],r[i],1,k[i]);
            printf("%d\n",Small+1);
        }
        if (O[i]==2)
        {
            ta=0;
            query_queue(1,n,l[i],r[i],1);
            printf("%d\n",val[query_rand(0,100000,k[i])]);
        }
        if (O[i]==3)
        {
            //if (a[l[i]]==k[i]) continue;
            modify(1,n,1,l[i],a[l[i]],-1);
            modify(1,n,1,l[i],k[i],1);
            a[l[i]]=k[i];
        }
        if (O[i]==4)
        {
            Small=0;
            query_randmin(1,n,l[i],r[i],1,k[i]);
            ta=0;
            query_queue(1,n,l[i],r[i],1);
            printf("%d\n",val[query_rand(0,100000,Small)]);
        }
        if (O[i]==5)
        {
            Max=0;
            query_randmax(1,n,l[i],r[i],1,k[i]);
            ta=0;
            query_queue(1,n,l[i],r[i],1);
            printf("%d\n",val[query_rands(0,100000,Max)]);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值