bzoj-3685 普通van Emde Boas树 权值线段树+快读!


普通van Emde Boas树

  题意不再赘述见题面:

设计数据结构支持:
1 x  若x不存在,插入x
2 x  若x存在,删除x
3    输出当前最小值,若不存在输出-1
4    输出当前最大值,若不存在输出-1
5 x  输出x的前驱,若不存在输出-1
6 x  输出x的后继,若不存在输出-1
7 x  若x存在,输出1,否则输出-1

 数据范围和操作数都是1e6级别的,所以普通的set或者treap用起来很方便的是会超时的,所以怎么解决呢?

因为拉的是有关权值线段树的专题,所以很容易想到这个,空间也不会爆,不用离散化,那么每个叶节点代表这个值是否出现过即可,每个节点储存当前区间的最值,只有操作5、6相对比较麻烦,如果前驱的话相当于求[0,x-1]中存在的数的最大值,反之求[x+1,n-1]的最小值,其他的操作比较简单。但写出来竟然TLE了,考虑到数据量比较大。于是加了个快读,8000+ms。荣神将递归改成循环式查找6000+ms,ORZ。

权值线段树+快速读:

const int N=1e6+10;
inline char nc()
{
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int sc()
{
    char ch=nc();
    int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
struct node
{
    int l,r,num,mi,ma;
} a[N<<2];
int n,m,c[N];
void pushup(int k)
{
    a[k].num=a[k*2].num+a[k*2+1].num;
    a[k].mi=min((a[k*2].num?a[k*2].mi:INF),(a[k*2+1].num?a[k*2+1].mi:INF));
    a[k].ma=max((a[k*2].num?a[k*2].ma:0),(a[k*2+1].num?a[k*2+1].ma:0));
}
void build(int l,int r ,int k)
{
    a[k].l=l,a[k].r=r,a[k].num=0,a[k].mi=INF,a[k].ma=0;
    if(l==r)
    {
        a[k].ma=a[k].mi=l;
        c[l]=0;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,2*k);
    build(mid+1,r,2*k+1);
}
void update(int id,int f,int k)//支持插入或删除
{
    if(a[k].l==id&&a[k].r==id)
    {
        if(f==1&&!a[k].num)
        {
            a[k].num^=1;
            c[id]=1;
        }
        else if(f==2&&a[k].num)
        {
            a[k].num^=1;
            c[id]=0;
        }
        return ;
    }
    int mid=(a[k].l+a[k].r)/2;
    if(id<=mid) update(id,f,k*2);
    else update(id,f,k*2+1);
    pushup(k);
}
int query(int l,int r,int f,int k)//求前驱或后继
{
    if(r<l) return INF;
    if(a[k].l==l&&a[k].r==r)
    {
        if(!a[k].num) return INF;
        return f==1?a[k].ma:a[k].mi;
    }
    int mid=(a[k].l+a[k].r)/2;
    if(r<=mid) return query(l,r,f,2*k);
    if(l>mid) return query(l,r,f,2*k+1);
    else
    {
        int tmp1=query(l,mid,f,2*k);
        int tmp2=query(mid+1,r,f,2*k+1);
        if(tmp1==INF&&tmp2!=INF) return tmp2;
        else if(tmp1!=INF&&tmp2==INF) return tmp1;
        return (f==1)?max(tmp1,tmp2):min(tmp1,tmp2);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        build(0,n,1);
        while(m--)
        {
            int x,y;
            x=sc();
            if(x==3||x==4)
            {
                if(!a[1].num) puts("-1");
                else printf("%d\n",x==3?a[1].mi:a[1].ma);
                continue;
            }
            y=sc();
            if(x==1||x==2) update(y,x,1);
            else if(x==7)
            {
                if(c[y]) puts("1");
                else puts("-1");
            }
            else
            {
                int tmp=INF;
                if(x==5) tmp=query(0,y-1,1,1);
                else tmp=query(y+1,n-1,2,1);
                if(tmp==INF) puts("-1");
                else printf("%d\n",tmp);
            }
        }
    }
    return 0;
}


荣神大佬:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 1000010
#define LEFT (pos << 1)
#define RIGHT (pos << 1 | 1)
using namespace std;
  
int range, asks, M;
int tree[MAX << 2];
  
inline void Modify(int pos, int c)
{
    tree[pos] += c;
    for (; pos; pos >>= 1)
        tree[pos] += c;
}
  
inline int GetMin(int pos)
{
    if (!tree[pos])
        return 0;
    while (pos <= M)
    {
        if (tree[LEFT])
            pos = LEFT;
        else
            pos = RIGHT;
    }
    return pos - M;
}
  
inline int GetMax(int pos)
{
    if (!tree[pos])
        return 0;
    while (pos <= M)
    {
        if (tree[RIGHT])
            pos = RIGHT;
        else
            pos = LEFT;
    }
    return pos - M;
}
  
inline int GetPred(int x)
{
    int pos = x + M;
    while (1)
    {
        if (pos == 1)
            return 0;
        if (pos & 1 && tree[pos ^ 1])
            return GetMax(pos ^ 1);
        pos >>= 1;
    }
    return 0;
}
  
inline int GetSucc(int x)
{
    int pos = x + M;
    while (1)
    {
        if (pos == 1)
            return 0;
        if (!(pos & 1) && tree[pos ^ 1])
            return GetMin(pos ^ 1);
        pos >>= 1;
    }
    return 0;
}
  
int main()
{
    cin >> range >> asks;
    for (M = 1; M <= range; M <<= 1)
        ;
    for (int x, flag, i = 1; i <= asks; ++i)
    {
        scanf("%d", &flag);
        if (flag == 1)
        {
            scanf("%d", &x);
            ++x;
            if (!tree[M + x])
                Modify(M + x, 1);
        }
        else if (flag == 2)
        {
            scanf("%d", &x);
            ++x;
            if (tree[M + x])
                Modify(M + x, -1);
        }
        else if (flag == 3)
            printf("%d\n", GetMin(1) - 1);
        else if (flag == 4)
            printf("%d\n", GetMax(1) - 1);
        else if (flag == 5)
        {
            scanf("%d", &x);
            ++x;
            printf("%d\n", GetPred(x) - 1);
        }
        else if (flag == 6)
        {
            scanf("%d", &x);
            ++x;
            printf("%d\n", GetSucc(x) - 1);
        }
        else
        {
            scanf("%d", &x);
            ++x;
            printf("%d\n", tree[x + M] ? 1 : -1);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值