Mex 相关

搞了一上午这些玩意儿,有点破防。

\(O(\log n)\) 在线求 \(\rm{mex}\)

考虑可持久化权值线段树,每个版本 \(i\) 维护了每个数在 \([1,i]\) 中最后出现的位置。

对于区间 \([l,r]\) 的查询,在可持久化权值线段树的第 \(r\) 个版本上查小于 \(l\) 的第一个数值,这里线段树上二分即可。

代码
#include <bits/stdc++.h>

void Freopen() {
    freopen("", "r", stdin);
    freopen("", "w", stdout);
}

using namespace std;
const int N = 2e5 + 10, M = 2e5 + 10, inf = 1e9, mod = 998244353;

int n, m;
int a[N];

struct sgt {
    int rt[N], tot;
    int mi[N * 20], ls[N * 20], rs[N * 20];

    void ins( int lk, int & k, int l, int r, int x, int v) {
        k = ++ tot;
        ls[k] = ls[lk], rs[k] = rs[lk];

        if (l == r) {
            mi[k] = v;
            return ;
        }

        int mid = (l + r) >> 1;
        x <= mid ? ins(ls[lk], ls[k], l, mid, x, v) : ins(rs[lk], rs[k], mid + 1, r, x, v);
        mi[k] = min(mi[ls[k]], mi[rs[k]]);
    }

    int ask( int k, int x, int l, int r) {
        if (l == r) return l;

        int mid = (l + r) >> 1;
        if (mi[ls[k]] < x) return ask(ls[k], x, l, mid);
        return ask(rs[k], x, mid + 1, r);
    }
} T;

signed main() {
    ios :: sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n >> m;

    for ( int i = 1; i <= n; i ++)
        cin >> a[i],
        T.ins(T.rt[i - 1], T.rt[i], 0, N, a[i], i);

    while (m --) {
        int l, r; cin >> l >> r;
        cout << T.ask(T.rt[r], l, 0, N) << '\n';
    }

    return 0;
}

求解极短、级长 \(\rm{mex}\) 区间。

对于一个区间 \([l,r]\),若不存在 \([l',r']\subsetneqq [l,r]\) 使得 \({\rm{mex}}(a[l',r'])={\rm{mex}}(a[l,r])\),则 \([l,r]\) 是一个极短 \(\rm mex\) 区间。

对于一个区间 \([l,r]\),若不存在 \([l,r]\subsetneqq [l‘,r’]\) 使得 \({\rm{mex}}(a[l',r'])={\rm{mex}}(a[l,r])\),则 \([l,r]\) 是一个极长 \(\rm mex\) 区间。

首先是结论,一个序列的极短、极长 \(\rm{mex}\) 区间个数都为 \(O(n)\)

记满足 \({\rm mex}(l,r)=x\) 的区间为 \({\rm mex}_x\) 区间。

考虑如何求出所有的极短 \(\rm mex\) 区间。

  • 对每个极短 \({\rm mex}_x\) 区间用 vector< pair< int, int> > 进行维护。
  • 预处理出极短 \({\rm mex}_0\)\({\rm mex}_1\) 区间。
  • 对于每个极短的 \({\rm mex}_{x-1}\) 区间 \([l,r]\),分别求出距离它左端点最近的 \(x-1\) 的位置 \(L\)、距离右端点最近的 \(x-1\) 的位置 \(R\),然后可以构成两个区间 \([l, R]\)\([L,r]\),对这两个区间计算出它们的 \(\rm mex\) 记为 \(x'\),然后丢进对应的 vector 中。
  • 对于 \({\rm mex}_x\) 的所有区间,把被完全包含的去掉,剩下的就是极短 \({\rm mex}_x\) 区间。
代码
cin >> n;

//M 表示值域
for ( int i = 0; i <= M; i ++) vec[i].push_back(0);

for ( int i = 1; i <= n; i ++)
    cin >> a[i],
    T.ins(T.rt[i - 1], T.rt[i], 0, N, a[i], i),
    vec[a[i]].push_back(i);

for ( int i = 0; i <= M; i ++) vec[i].push_back(n + 1);

for ( int i = 1; i <= n; i ++) {
    if (a[i] == 0) Mex[1].push_back({i, i});
    else Mex[0].push_back({i, i});
}

for ( int i = 1; i <= M; i ++) {
    for ( auto [l, r] : Mex[i - 1]) {
	    int L = * prev(upper_bound(vec[i - 1].begin(), vec[i - 1].end(), l));
        int R = * lower_bound(vec[i - 1].begin(), vec[i - 1].end(), r);
        
        // ask(l,r) 就是 [l,r] 的 mex
        if (L != 0) Mex[ask(L, r)].push_back({L, r});
        if (R != n + 1) Mex[ask(l, R)].push_back({l, R});
    }   

    sort(Mex[i].begin(), Mex[i].end(), [&]( pair< int, int> a, pair< int, int> b) {
        return a.first == b.first ? a.second < b.second : a.first > b.first;
    });

    vector< pair< int, int> > tmp;
    int lst = inf;

    for ( auto [l, r] : Mex[i]) {
        if (lst > r) tmp.push_back({l, r});
        lst = min(lst, r);
    }

    swap(Mex[i], tmp); 
}

求出极短后,求极长就很简单了。

对于一个极短 \({\rm mex}_x\) 区间 \([l,r]\),分别求出距离它左端点最近的 \(x\) 的位置 \(L\)、距离右端点最近的 \(x\) 的位置 \(R\),那么 \([L+1,R-1]\) 就是它的极长区间。

[CEOI 2025] Equal Mex

\(v_i\)\(1\) 后就变成了 \(\rm mex\)

分析一下发现,如果 \(k\) 是一个完美的划分,那么 \(k-1\) 也一定是,显然合并两个 \(\rm mex\) 相同的序列 \(\rm mex\) 依旧相同。

同时,对于一个区间 \([l,r]\),若它的 \(\rm mex\)\(x\),那划分出来的所有区间的 \(\rm mex\) 也要为 \(x\)

那么问题转化成对一个 \(\rm mex\)\(x\) 的区间,求出一个最大的划分使得每个段的 \(\rm mex\) 都是 \(x\)

那么就很自然地想到求出极短 \(\rm mex\) 区间。

对于一个 \({\rm mex}=x\) 的询问,考虑所有的极短 \({\rm mex}_x\) 区间。

也就是要在所有的极短 \({\rm mex}_x\) 区间中选出最多的区间使得都在 \([l,r]\) 内,且两两无交。

因为极短区间是没有包含关系的,所以每次贪心地选最左边且无交的区间即可。

这里用倍增优化。

代码
#include <bits/stdc++.h>

void Freopen() {
    freopen("", "r", stdin);
    freopen("", "w", stdout);
}

using namespace std;
const int N = 6e5 + 10, M = 4e5, inf = 1e9, mod = 998244353;

int n, q;
int a[N];

struct sgt {
    int rt[N], tot;
    int mi[N * 20], ls[N * 20], rs[N * 20];

    void ins( int lk, int & k, int l, int r, int x, int v) {
        k = ++ tot;
        ls[k] = ls[lk], rs[k] = rs[lk];

        if (l == r) {
            mi[k] = v;
            return ;
        }

        int mid = (l + r) >> 1;
        x <= mid ? ins(ls[lk], ls[k], l, mid, x, v) : ins(rs[lk], rs[k], mid + 1, r, x, v);
        mi[k] = min(mi[ls[k]], mi[rs[k]]);
    }

    int ask( int k, int x, int l, int r) {
        if (l == r) return l;

        int mid = (l + r) >> 1;
        if (mi[ls[k]] < x) return ask(ls[k], x, l, mid);
        return ask(rs[k], x, mid + 1, r);
    }
} T;

int ask( int l, int r) {
    return T.ask(T.rt[r], l, 0, M);
}

struct que {
    int l, r, id;
} ;

vector< que> qry[N];

vector< pair< int, int> > Mex[N];
vector< int> vec[N];

int f[22][N];
int ans[N];
vector< int> Ans;

std::vector<int> solve(
    int n, std::vector<int>& v,
    int q, std::vector<std::pair<int, int>>& queries) {
    for ( int i = 0; i <= M; i ++) vec[i].push_back(0);

    for ( int i = 1; i <= n; i ++)
        a[i] = v[i - 1] - 1,
        T.ins(T.rt[i - 1], T.rt[i], 0, M, a[i], i),
        vec[a[i]].push_back(i);

    for ( int i = 0; i <= M; i ++) vec[i].push_back(n + 1);

    for ( int i = 1; i <= q; i ++) {
        int l = queries[i - 1].first, r = queries[i - 1].second;
        qry[ask(l, r)].push_back({l, r, i});
    }

    for ( int i = 1; i <= n; i ++) {
        if (a[i] == 0) Mex[1].push_back({i, i});
        else Mex[0].push_back({i, i});
    }

    for ( int i = 1; i <= M; i ++) {
        for ( auto [l, r] : Mex[i - 1]) {
			int L = * prev(upper_bound(vec[i - 1].begin(), vec[i - 1].end(), l));
            int R = * lower_bound(vec[i - 1].begin(), vec[i - 1].end(), r);

            if (L != 0) Mex[ask(L, r)].push_back({L, r});
            if (R != n + 1) Mex[ask(l, R)].push_back({l, R});
        }   

        sort(Mex[i].begin(), Mex[i].end(), [&]( pair< int, int> a, pair< int, int> b) {
            return a.first == b.first ? a.second < b.second : a.first > b.first;
        });

        vector< pair< int, int> > tmp;
        int lst = inf;

        for ( auto [l, r] : Mex[i]) {
            if (lst > r) tmp.push_back({l, r});
            lst = min(lst, r);
        }

        swap(Mex[i], tmp); 
        reverse(Mex[i].begin(), Mex[i].end());
    }

    for ( int i = 0; i <= M + 1; i ++) {
        int len = (int)Mex[i].size();
        
        int ptr = 1;
        for ( int k = 1; k <= len; k ++) {
            while (Mex[i][ptr - 1].first <= Mex[i][k - 1].second && ptr <= len) ptr ++;
            if (ptr <= len) f[0][k] = ptr;
        }

        for ( int k = 1; k < 22; k ++)
            for ( int j = 1; j <= len; j ++)
                f[k][j] = f[k - 1][f[k - 1][j]];

        for ( auto [l, r, id] : qry[i]) {
            int k = lower_bound(Mex[i].begin(), Mex[i].end(), make_pair(l, 0)) - Mex[i].begin() + 1;

            if (Mex[i][k - 1].second > r) {
                ans[id] = 0;
                continue ;
            }

            int res = 1;

            for ( int j = 21; j >= 0; j --) {
                if (f[j][k] && Mex[i][f[j][k] - 1].second <= r) {
                    k = f[j][k], res += (1 << j);
                }
            }

            ans[id] = res;
        }

        for ( int k = 0; k < 22; k ++)
            for ( int j = 1; j <= len; j ++)
                f[k][j] = 0;
    }

    for ( int i = 1; i <= q; i ++) Ans.push_back(ans[i]);
    return Ans;
}
posted @ 2025-10-10 15:32  咚咚的锵  阅读(22)  评论(0)    收藏  举报