@bzoj - 3339@ Rmq Problem


@description@

Input

Output

Sample Input
7 5
0 2 1 0 1 3 2
1 3
2 3
1 4
3 6
2 7
Sample Output
3
0
3
2
4

Hint

@solution@

在线不大好搞,因为 mex 函数不大支持合并,也没有比较好用的性质。
离线,如果采用每次加入一个数的方法也不大好搞。

但是反过来,假如我现在的区间的 mex 是 ans,删掉一个 a[x] 后如果剩下的区间没有与 a[x] 相同的数,可以得到新的 ans' = min(ans, a[x])。
这个证明还是比较直观的。也就是说我们可以写莫队了。我们接下来的算法就利用这个性质。

考虑固定左端点 l,维护以 l~n 中的下标作为右端点的 mex 值。记录 nxt[l] 表示 l 之后最近的与 a[l] 相同的位置。
每次从 l 转移到 l + 1 时就直接对区间 [l, nxt[l] - 1] 进行区间对 a[l] 取 min 的操作即可。
因为 mex 值是单调的(越远越大),这个很容易就线段树维护出来了。查询直接离线在线段树上查就可以了。
也许可以强制在线然后用可持久化线段树搞。

@accepted code@

#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 200000;
vector<pair<int, int> >vec[MAXN + 5];
int n, m, a[MAXN + 5], ans[MAXN + 5];
int mex[MAXN + 5];
struct segtree{
    struct node{
        int l, r, mtag;
    }t[4*MAXN + 5];
    void build(int x, int l, int r) {
        t[x].l = l, t[x].r = r, t[x].mtag = MAXN;
        if( l == r ) return ;
        int mid = (l + r) >> 1;
        build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
    }
    void modify(int x, int l, int r, int k) {
        if( l > t[x].r || r < t[x].l )
            return ;
        if( l <= t[x].l && t[x].r <= r ) {
            t[x].mtag = min(t[x].mtag, k);
            return ;
        }
        modify(x << 1, l, r, k), modify(x << 1 | 1, l, r, k);
    }
    int query(int x, int p) {
        if( t[x].l == t[x].r ) return min(t[x].mtag, mex[p]);
        int mid = (t[x].l + t[x].r) >> 1;
        if( p <= mid ) return min(t[x].mtag, query(x << 1, p));
        else return min(t[x].mtag, query(x << 1 | 1, p));
    }
}T;
bool tag[MAXN + 5];
int nxt[MAXN + 5], adj[MAXN + 5];
void solve() {
    int pos = 0;
    for(int i=1;i<=n;i++) {
        tag[a[i]] = true;
        while( tag[pos] ) pos++;
        mex[i] = pos;
    }
    for(int i=n;i>=1;i--) {
        nxt[i] = (adj[a[i]] ? adj[a[i]] : n + 1);
        adj[a[i]] = i;
    }
    T.build(1, 1, n);
    for(int i=1;i<=n;i++) {
        for(int j=0;j<vec[i].size();j++)
            ans[vec[i][j].second] = T.query(1, vec[i][j].first);
        T.modify(1, 1, nxt[i]-1, a[i]);
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++)
        scanf("%d", &a[i]);
    for(int i=1;i<=m;i++) {
        int L, R; scanf("%d%d", &L, &R);
        vec[L].push_back(make_pair(R, i));
    }
    solve();
    for(int i=1;i<=m;i++)
        printf("%d\n", ans[i]);
}

@details@

不得不说,通过删除一个数来维护新的区间信息的题目还是比较少见的。因为大多数题目都是维护加入一个数的情况。

人类智慧。

posted @ 2019-08-05 17:11  Tiw_Air_OAO  阅读(113)  评论(0编辑  收藏  举报