思路:
七个操作显然都可以用
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;
}