tyvj4866 摆摊 线段树MEX

http://www.elijahqi.win/archives/916

真的感觉自己real弱啊 在zhx的代码帮助下理解了这个内容 我不知道自己这么低的智商未来会不会有出路

有一些必要的解释,放在了程序中

next[i][0]表示 在序列a中下标为i+1到m中最近一次出现a[i]-1的位置

关于这个线段树的使用

线段树其实我们是查找截至到右端点,我们现在可用的最小值

我每次处理l的时候 在l+1到min(next[l][1],next[l][2])之间 都可以使用a[l]这个地方

官方的题解:

100分做法:
记 nxt(x,y) 为下标从 x+1 开始 a 数组第一次出现 y 这个数字的位置。
离线处理这个问题,考虑 L 从 1 到 M 扫过去,在扫过去的同时维护 R 等于 L 到 M 的答案。
然后把询问也按照 L 排序,就可以在 L 扫过去的过程依次回答询问。 L=1 的时候,可以暴力处理出来 R 等于每个值的答案。
然后 L 每次 +1。
L 从 x 变成 x+1 的过程,对于维护 R 等于 x+1 到 M 的答案来 说,变化是少了 a[x] 这一个数字,对于 R>=nxt(x,a[x]) 的数字是没有影 响的。对于 R 在 x 到 min(nxt(x,a[x]),nxt(x,a[x]+1)) 之间的答案由于 a[x] 被去掉,并且也没有 a[x]+1 这个数字,a[x] 在这一段就是一个可能的答 案 (换句话说 R 在这些位置的答案应该和 a[x] 取 min);对于 R 在 x 到 min(nxt(x,a[x]),nxt(x,a[x]-1)) 之间的答案由于 a[x] 被去掉,并且没有 a[x]-1 这个数字,a[x]-1 在这一段就是一个可能的答案 (换句话说 R 在这些位置的 答案应该和 a[x]-1 取 min)。发现去掉 a[x] 并没有其他影响。
nxt(x,a[x]),nxt(x,a[x]+1),nxt(x,a[x]-1) 可以 O(n) 预处理。

#include<cstdio>
#include<algorithm>
#define N 220000
#define inf 0x7f7f7f7f
struct node{
    int l,r,id;
}q[N];
struct node1{
    int l,r,left,right,min;
}tree[N<<2];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline bool cmp(node a,node b){return a.l<b.l;}
inline int min(int x,int y){
    return x<y?x:y;
}
int next[N][3];//next[i][0]表示[i+1]号位置 到结束第一次出现a[i]-1的地方,其余同理 
int last[N],ans[N],mex[N],num,n,m,q1,a[N],root; bool map[N];
inline void update(int x){
    int l=tree[x].left,r=tree[x].right;
    tree[x].min=min(tree[l].min,tree[r].min);
}
void build(int &x,int l,int r){
    x=++num;tree[x].l=l;tree[x].r=r;tree[x].min=inf;
    if (l==r) {tree[x].min=mex[l] ;return;}
    int mid=l+r>>1;
    build(tree[x].left,l,mid);build(tree[x].right,mid+1,r);
    //update(x);
}
void insert1(int x,int l,int r,int v){
    if (l<=tree[x].l&&r>=tree[x].r){tree[x].min=min(tree[x].min,v);return;}
    int mid=tree[x].l+tree[x].r>>1;
    if (l<=mid) insert1(tree[x].left,l,r,v);
    if (r>mid) insert1(tree[x].right,l,r,v);
    //update(x);
}
int query(int x,int l){
    if (tree[x].l==tree[x].r) return tree[x].min;
    int mid=tree[x].l+tree[x].r>>1;int min1=tree[x].min;
    if (l<=mid) min1=min(min1,query(tree[x].left,l));
    if (l>mid) min1=min(min1,query(tree[x].right,l));
    return min1;
}
void print(int x){
    if (tree[x].left) print(tree[x].left);
    printf("%d %d %d\n",tree[x].l,tree[x].r,tree[x].min);
    if (tree[x].right) print(tree[x].right);
}
int main(){
    freopen("stall.in","r",stdin);
    n=read();m=read();q1=read();int tmp=1;
    for (int i=1;i<=m;++i) {
        a[i]=read(),last[i]=m+1;map[a[i]]=true;
        while (map[tmp]||map[tmp+1]) ++tmp;
        mex[i]=tmp;//表示已经放了前i个 然后我最好的答案可以取在哪里 
    }
    //for (int i=1;i<=m;++i) printf("%d ",mex[i]);
    for (int i=m;i>=1;--i){
        next[i][0]=last[a[i]-1];next[i][1]=last[a[i]];next[i][2]=last[a[i]+1];last[a[i]]=i;
    }
    for (int i=1;i<=q1;++i) q[i].l=read(),q[i].r=read(),q[i].id=i;
    std::sort(q+1,q+q1+1,cmp);
    build(root,1,m);int l=1;//printf("asdf%d ",tree[root].min);
    for (int i=1;i<=q1;++i){
        while(l<q[i].l){
            if (a[l]+1<=n) insert1(root,l+1,min(next[l][1],next[l][2])-1,a[l]);
            if (a[l]-1>=1) insert1(root,l+1,min(next[l][1],next[l][0])-1,a[l]-1);++l;
        } 
    //  print(root);printf("asdfasd\n");
        ans[q[i].id]=query(root,q[i].r);
    }
    for (int i=1;i<=q1;++i) printf("%d %d\n",ans[i],ans[i]+1);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值