BZOJ 3685 zkw线段树 || 权值线段树

题目链接


思路:
七个操作显然都可以用 T r e a p Treap Treap来维护,但编程复杂度很高,而且似乎此题数据也故意卡 T r e a p Treap Treap

前几个操作都很简单,主要复杂于前驱和后继的高效维护。

考虑建立一棵权值线段树,对于值 v a l val val的前驱和线段树当前节点 r t rt rt
v a l val val r t rt rt的左儿子区间,则直接递归到左儿子去找前驱。
v a l val val r t rt rt的右儿子区间,则前驱有两种可能,一是在右儿子区间中 v a l val val值的前一个值,二是可能为左儿子区间的最大值。

分情况讨论即可,对于后继同理。

加上读入挂,8000ms勉强卡过。

这里写图片描述


而上面3000ms的代码,则使用的是zkw线段树。
因为此题只需要维护前缀和,且自下往上的查找前驱和后继的方式更加快速,并且zkw线段树在内存和编程复杂度上,均比普通的权值线段树简单很多。

代码:
普通权值线段树:

#include<cstdio>
using namespace std;
#define lson rt<<1
#define rson rt<<1|1
inline 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;
}
 
const int A = 1e6 + 10;
class Seg_Tree{
public:
    int l,r,val;
}Tree[A<<2];
int n,m,op,x;
 
inline void push_up(int rt){Tree[rt].val = Tree[lson].val + Tree[rson].val;}
 
void build(int rt,int l,int r){
    Tree[rt].l = l,Tree[rt].r = r;Tree[rt].val = 0;
    if(l == r) return;
    int mid = (l+r)>>1;
    build(lson,l,mid);build(rson,mid+1,r);
    push_up(rt);
}
 
void update(int rt,int v,int c){
    int l = Tree[rt].l,r = Tree[rt].r;
    if(l == r){Tree[rt].val = c;return;}
    int mid = (l+r)>>1;
    if(v<=mid) update(lson,v,c);
    else       update(rson,v,c);
    push_up(rt);
}
 
int Mn(int rt){
    if(!Tree[rt].val) return -1;
    int l = Tree[rt].l,r = Tree[rt].r;
    if(l == r) return l;
    if(Tree[lson].val) return Mn(lson);
    else               return Mn(rson);
}
 
int Mx(int rt){
    if(!Tree[rt].val) return -1;
    int l = Tree[rt].l,r = Tree[rt].r;
    if(l == r) return l;
    if(Tree[rson].val) return Mx(rson);
    else               return Mx(lson);
}
 
int Find(int rt,int val){
    int l = Tree[rt].l,r = Tree[rt].r;
    if(l == r){
        if(Tree[rt].val) return 1;
        else             return -1;
    }
    int mid = (l+r)>>1;
    if(val<=mid) return Find(lson,val);
    else         return Find(rson,val);
}
 
int Find_pr(int rt,int val){
    if(val<0 || !Tree[rt].val) return -1;
    int l = Tree[rt].l,r = Tree[rt].r;
    if(l == r) return l;
    int mid = (l+r)>>1;
    if(val <= mid) return Find_pr(lson,val);
    else{
        int tem = Find_pr(rson,val);
        if(tem == -1) return Mx(lson);
        return tem;
    }
}
 
int Find_su(int rt,int val){
    if(!Tree[rt].val) return -1;
    int l = Tree[rt].l,r = Tree[rt].r;
    if(l == r) return l;
    int mid = (l+r)>>1;
    if(val>mid) return Find_su(rson,val);
    else{
        int tem = Find_su(lson,val);
        if(tem == -1) return Mn(rson);
        return tem;
    }
}
 
int main(){
    n = read();m = read();
    build(1,0,n);
    for(int i=1 ;i<=m ;i++){
        op = read();
        if(op == 1){x=read();update(1,x,1);}else
        if(op == 2){x=read();update(1,x,0);}else
        if(op == 3){printf("%d\n",Mn(1));}  else
        if(op == 4){printf("%d\n",Mx(1));}  else
        if(op == 5){x=read();printf("%d\n",Find_pr(1,x-1));}else
        if(op == 6){x=read();printf("%d\n",Find_su(1,x+1));}else
        if(op == 7){x=read();printf("%d\n",Find(1,x));}
    }
    return 0;
}


zkw线段树:

#include<cstdio>
using namespace std;
#define lson rt<<1
#define rson rt<<1|1
#define fa   rt>>1
inline 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;
}
 
const int A = 1<<20;
int Tree[A<<1|1],M,n,m,op,x;
bool vis[A];
 
void update(int rt,int v){for(rt+=M;rt;rt=fa) Tree[rt]+=v;}
int Mn(int rt){for(;rt<=M;rt=Tree[lson]?lson:rson);return rt-M-1;}
int Mx(int rt){for(;rt<=M;rt=Tree[rson]?rson:lson);return rt-M-1;}
int Find_pr(int rt){for(rt+=M ;rt!=1 ;rt=fa){if((rt&1) && Tree[rt^1]) return Mx(rt^1);}return -1;}
int Find_su(int rt){for(rt+=M ;rt!=1 ;rt=fa){if(!(rt&1)&& Tree[rt^1]) return Mn(rt^1);}return -1;}
 
int main(){
    n = read();m = read();
    for(M=1 ;M<=n ;M<<=1);
    for(int i=1 ;i<=m ;i++){
        op = read();
        if(op==1){if(!vis[x=read()+1]) vis[x]=1,update(x,1);}else
        if(op==2){if(vis[x=read()+1]) vis[x]=0,update(x,-1);}else
        if(op==3){printf("%d\n",Tree[1]?Mn(1):-1);}else
        if(op==4){printf("%d\n",Tree[1]?Mx(1):-1);}else
        if(op==5){printf("%d\n",Find_pr(read()+1));}else
        if(op==6){printf("%d\n",Find_su(read()+1));}else
        if(op==7){printf("%d\n",vis[read()+1]?1:-1);}
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值