• luogu 4137 Rmq Problem / mex


    题意:
    n个数,m次询问,每次询问一个区间的mex(不在区间内的最小自然数)。
    思路:
    离线+线段树
    经典离线数据结构思维题
    对询问离线处理,按左端点分类。
    从左到右枚举左端点,需要维护出以当前枚举下标i为左端点的时候,每个点[i..n]作为右端点的答案,这样查询的时候,只需要查询对应右端点位置的值即可。
    关键在于左端点移动时对答案带来的改变。
    当左端点向右移动一个单位时,考虑对哪些区间的答案产生影响。
    注意到如果当前值为a[i],a[i]下一次出现的位置为nxt[i],那么对[nxt[i]..n]范围内的点,答案不会产生变化,因为实际区间内出现的元素没有发生改变。
    因此实际发生改变的区间为[i..nxt[i]-1]。
    那么接下来分析对这些区间带来什么样的变化。
    变化在于,没有了a[i]这个元素,那么如果原先的值小于等于a[i],由mex的定义,答案不发生改变;如果原先的答案大于a[i],那么此时a[i]成为了最新的没在区间内出现的最小的数了。
    因此这些位置的答案都需要变成a[i]。
    也就是说,我们需要做的事情其实是,维护一个数组,然后对某个区间取min,然后查询单点的值。
    那么用线段树来做这件事再合适不过。
    一开始脑残傻逼了以为是吉司机线段树,后来意识到了因为没有求区间和,所以不需要用吉司机那一套。
    直接对每个区间打个lazy标记即可。
    维护区间min的地方写残了WA了两发。
    这套做法能够起作用的关键在于,随着左端点的单位移动,数组维护的答案变化是可以用数据结构很好地维护出来的。
    是一个非常重要的思路。
    枚举左端点,维护一个数组,记录从当前左端点到每个右端点的答案,再考虑左端点移动对答案带来的影响。

    #include<bits/stdc++.h>
    #define lson(p) p<<1
    #define rson(p) p<<1|1
    using namespace std;
    const int maxn=2e5+10;
    const int INF=0x3f3f3f3f;
    int a[maxn],c[maxn],nxt[maxn];
    int n,m,l,r;
    int ans[maxn];
    vector<pair<int,int> > q[maxn];
    map<int,bool> mp;
    map<int,int> last;
    struct node
    {
        int mn;
        int lazy;
        void update(int v)
        {
            mn=min(v,mn);
            lazy=min(lazy,v);
        }
    }b[4*maxn];
    void pushup(int p)
    {
        b[p].mn=min(b[lson(p)].mn,b[rson(p)].mn);
    }
    void build(int p,int l,int r)
    {
        b[p].lazy=INF;
        if (l==r) {
            b[p].mn=c[l];
            return ;
        }
        int mid=(l+r)>>1;
        build(lson(p),l,mid);
        build(rson(p),mid+1,r);
        pushup(p);
    }
    void pushdown(int p)
    {
        if (b[p].lazy!=INF) {
            b[lson(p)].update(b[p].lazy);
            b[rson(p)].update(b[p].lazy);
            b[p].lazy=INF;
        }
    }
    void update(int p,int l,int r,int L,int R,int val)
    {
        if (L<=l&&r<=R) {
            b[p].mn=min(b[p].mn,val);
            b[p].lazy=min(b[p].lazy,val);
            return ;
        }
        pushdown(p);
        int mid=(l+r)>>1;
        if (L<=mid) update(lson(p),l,mid,L,R,val);
        if (R>mid) update(rson(p),mid+1,r,L,R,val);
        pushup(p);
    }
    int query(int p,int l,int r,int pos)
    {
        if (l==r) return b[p].mn;
        int mid=(l+r)>>1;
        pushdown(p);
        if (pos<=mid) return query(lson(p),l,mid,pos);
        else return query(rson(p),mid+1,r,pos);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        for (int i=1;i<=n;i++) nxt[i]=-1;
        for (int i=1;i<=n;i++) {
            if (last.count(a[i])) {
                nxt[last[a[i]]]=i;
            }
            last[a[i]]=i;
        }
        for (int i=1;i<=m;i++) {
            scanf("%d%d",&l,&r);
            q[l].push_back(make_pair(r,i));
        }
        for (int i=1;i<=n;i++) {
            mp[a[i]]=true;
            int tem=c[i-1];
            while (mp[tem]) tem++;
            c[i]=tem;
        }
        build(1,1,n);
        for (int i=1;i<=n;i++) {
            for (int j=0;j<(int)q[i].size();j++) {
                int x=q[i][j].first,y=q[i][j].second;
                ans[y]=query(1,1,n,x);
            }
            if (nxt[i]!=-1) update(1,1,n,i,nxt[i]-1,a[i]);
            else update(1,1,n,i,n,a[i]);
        }
        for (int i=1;i<=m;i++) printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    Python深拷贝和浅拷贝解析
    python中count函数的用法
    Jenkins + gitlab + maven 自动打包部署项目
    nio和bio得区别
    nginx负载均衡的5种策略
    接口测试常见bug
    接口自动化面试4
    pass 语句
    if 语句
    while循环
  • 原文地址:https://www.cnblogs.com/xyw5vplus1/p/13778988.html
Copyright © 2020-2023  润新知