CF1436E Complicated Computations 题解

题目链接:CF 或者 洛谷

关于 \(mex\) 问题是一个比较久远的问题,有很多经典的方法去解决。本题的 \(mex\) 是从正整数开始的,不要忽略掉。

来讲讲常见的两种解决方案,首先回到题目所问,如果我们暴力地询问:

\(1,2,3,4,.....mex\) 是否都能由原数组构造出来,对于 \(i\) 如果它可以由原数组构造出来,那么即有某个子数组 \(mex(l,r)=i\),那么首先根据 \(mex\) 的定义,我们可以知道 \([l,r]\) 上一定不能含有 \(i\),其次,在这个基础上,\([l,r]\) 尽量大,才也有可能保证 \(mex=i\),具体的你需要知道 \(mex=i\) 的子数组的基本性质:

  1. \(mex=i\),则该序列不包括 \(i\),否则可以拓展为 \(mex=i+1\)

  2. \(mex\) 关于 \(len\) 单调不降,即元素个数越多,\(mex\) 越可能变大。

  3. \(mex\) 不包含 \(i\),则 \(mex \le i\),这个很显而易见。

其实这三条性质告诉了我们一种方式:

如果构造出 \(mex=i\) 的可能方式?我们找到所以 \(a_j=i\),以这些 \(j\) 划分,它会形成若干个极长段。

为啥要选择两个 \(i\) 之间的完整一段,这是基于第二点和第三点,显然不包括 \(i\) 且最长的段才有可能构造出 \(mex=i\)。我们注意到枚举 \(mex\) 这样的分割点总数至多为 \(n\) 个,因为数组至多只有 \(n\) 个位置,每个位置至多出现在某一种 \(mex\) 中。也就是说至多有 \(O(n)\) 段需要求 \(mex\),这个就转化为了非常简单与经典的 \(区间mex\) 询问。

解法一

普通莫队+值域分块平衡复杂度

建议先学习:值域分块系统级教学

对于莫队怎么求区间 \(mex\),这个问题很简单。考虑权值域,我们每次莫队会往权值域内增加或者删除一个数,\(cnt[x]++,cnt[x]--\),而最暴力地询问 \(mex\) 则为从 \(1\) 开始,一直到 \(mex\),其中 \(mex\) 是第一个 \(cnt[mex]==0\) 的数。

那么这样的复杂度是,插入和删除是 \(O(1)\),查询是 \(O(n)\),如果换成树状数组的话,可以做到 \(O(\log{V})\) 插入和删除,\(O(\log{V})\) 查询。查询的话这里要说下如何使用线段树或者树状数组做到单 \(\log{V}\) 的复杂度查询,我们只需要维护一个区间最小值,然后二分答案,找第一个 \(cnt=0\) 的点,即为找最小值 \(=0\) 的第一个点即可。

所以上述复杂度并不合理,我们需要 \(O(1)\) 修改,查询可以要求劣一点,\(O(\sqrt{V})\) 的查询就行了,这显然值域分块就能办到了,每次单点加值域块和值域点,查询枚举值域块找答案就行了。

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

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")

// #define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>

#endif

using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

template <typename T>
int disc(T* a, int n)
{
    return unique(a + 1, a + n + 1) - (a + 1);
}

template <typename T>
T lowBit(T x)
{
    return x & -x;
}

template <typename T>
T Rand(T l, T r)
{
    static mt19937 Rand(time(nullptr));
    uniform_int_distribution<T> dis(l, r);
    return dis(Rand);
}

template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
    return (a % b + b) % b;
}

template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
    a %= c;
    T1 ans = 1;
    for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
    return modt(ans, c);
}

template <typename T>
void read(T& x)
{
    x = 0;
    T sign = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    x *= sign;
}

template <typename T, typename... U>
void read(T& x, U&... y)
{
    read(x);
    read(y...);
}

template <typename T>
void write(T x)
{
    if (typeid(x) == typeid(char))return;
    if (x < 0)x = -x, putchar('-');
    if (x > 9)write(x / 10);
    putchar(x % 10 ^ 48);
}

template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
    write(x), putchar(c);
    write(c, y...);
}


template <typename T11, typename T22, typename T33>
struct T3
{
    T11 one;
    T22 tow;
    T33 three;

    bool operator<(const T3 other) const
    {
        if (one == other.one)
        {
            if (tow == other.tow)return three < other.three;
            return tow < other.tow;
        }
        return one < other.one;
    }

    T3() { one = tow = three = 0; }

    T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
    {
    }
};

template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
    if (x < y)x = y;
}

template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
    if (x > y)x = y;
}

constexpr int N = 2e5 + 10;
int pos[N];

struct Mo
{
    int l, r;

    bool operator<(const Mo& other) const
    {
        return pos[l] ^ pos[other.l] ? pos[l] < pos[other.l] : pos[l] & 1 ? r < other.r : r > other.r;
    }
} node[N];

int n, q;
vector<int> t[N];
int a[N];
bool vis[N];
int val[N], valPos[N];
int siz, cnt, s[N], e[N];
int Cnt[N];

inline void update(const int x, const int v)
{
    const int id = pos[x];
    val[id] += v, valPos[x] += v;
}

inline void add(const int x)
{
    if (Cnt[x]++ == 0)update(x, 1);
}

inline void del(const int x)
{
    if (--Cnt[x] == 0)update(x, -1);
}

inline int queryMex()
{
    int id = 1;
    while (id <= cnt and val[id] == e[id] - s[id] + 1)id++;
    if (id > cnt)return n + 1;
    int ans = s[id];
    while (ans <= e[id] and valPos[ans])ans++;
    return ans;
}

inline void solve()
{
    cin >> n;
    siz = sqrt(n);
    cnt = (n + siz - 1) / siz;
    forn(i, 1, n)cin >> a[i], t[a[i]].push_back(i), pos[i] = (i - 1) / siz + 1;
    forn(i, 1, cnt)s[i] = (i - 1) * siz + 1, e[i] = i * siz;
    e[cnt] = n;
    forn(i, 1, n+1)
    {
        int l = 1;
        for (const auto r : t[i])
        {
            if (l == r)
            {
                l = r + 1;
                continue;
            }
            node[++q] = Mo(l, r - 1), l = r + 1;
        }
        if (l < n)node[++q] = Mo(l, n);
    }
    sortArr(node, q);
    int l = 1, r = 0;
    forn(i, 1, q)
    {
        const auto [L,R] = node[i];
        while (l < L)del(a[l++]);
        while (l > L)add(a[--l]);
        while (r < R)add(a[++r]);
        while (r > R)del(a[r--]);
        vis[queryMex()] = true;
    }
    int mex = 1;
    while (vis[mex])mex++;
    cout << mex;
}

signed int main()
{
    // MyFile
    Spider
    //------------------------------------------------------
    // clock_t start = clock();
    int test = 1;
    //    read(test);
    // cin >> test;
    forn(i, 1, test)solve();
    //    while (cin >> n, n)solve();
    //    while (cin >> test)solve();
    // clock_t end = clock();
    // cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

\[时间复杂度为:\ O(n\sqrt{n}) \]

解法二

回滚莫队

太经典了,区间 \(mex\) 问题回滚莫队也能解决,也是主要解决途径,具体的,我们观察到,如果往一个权值域增加一个数,那么我们的 \(mex\) 显然需要暴力地更新到最新 \(mex\),因为可能一开始已经有若干值了。

如图所示,如果 \(+\) 上一个数,那么很容易发现,这个 \(mex\) 是跳跃式更新,但是,删除如果一个数 消失了,且这个数 \(<mex\),则 \(mex=这个数\),这个更新则是 \(O(1)\) 的,所以我们可以使用只删不加的回滚莫队即可。

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

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")

// #define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>

#endif

using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

template <typename T>
int disc(T* a, int n)
{
    return unique(a + 1, a + n + 1) - (a + 1);
}

template <typename T>
T lowBit(T x)
{
    return x & -x;
}

template <typename T>
T Rand(T l, T r)
{
    static mt19937 Rand(time(nullptr));
    uniform_int_distribution<T> dis(l, r);
    return dis(Rand);
}

template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
    return (a % b + b) % b;
}

template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
    a %= c;
    T1 ans = 1;
    for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
    return modt(ans, c);
}

template <typename T>
void read(T& x)
{
    x = 0;
    T sign = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    x *= sign;
}

template <typename T, typename... U>
void read(T& x, U&... y)
{
    read(x);
    read(y...);
}

template <typename T>
void write(T x)
{
    if (typeid(x) == typeid(char))return;
    if (x < 0)x = -x, putchar('-');
    if (x > 9)write(x / 10);
    putchar(x % 10 ^ 48);
}

template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
    write(x), putchar(c);
    write(c, y...);
}


template <typename T11, typename T22, typename T33>
struct T3
{
    T11 one;
    T22 tow;
    T33 three;

    bool operator<(const T3 other) const
    {
        if (one == other.one)
        {
            if (tow == other.tow)return three < other.three;
            return tow < other.tow;
        }
        return one < other.one;
    }

    T3() { one = tow = three = 0; }

    T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
    {
    }
};

template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
    if (x < y)x = y;
}

template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
    if (x > y)x = y;
}

constexpr int N = 2e5 + 10;
int pos[N];

struct Mo
{
    int l, r;

    bool operator<(const Mo& other) const
    {
        return pos[l] ^ pos[other.l] ? pos[l] < pos[other.l] : r > other.r;
    }
} node[N];

int n, q;
vector<int> t[N];
int a[N];
bool vis[N];
int curr = 1;
int siz, cnt, s[N], e[N];
int Cnt[N];
stack<int> back;

inline void del(const int x, const bool isBack = false)
{
    if (--Cnt[x] == 0 and x < curr)curr = x;
    if (isBack)back.push(x);
}

int tmp[N];

inline void solve()
{
    cin >> n;
    siz = sqrt(n);
    cnt = (n + siz - 1) / siz;
    forn(i, 1, n)cin >> a[i], t[a[i]].push_back(i), pos[i] = (i - 1) / siz + 1;
    forn(i, 1, cnt)s[i] = (i - 1) * siz + 1, e[i] = i * siz;
    e[cnt] = n;
    forn(i, 1, n+1)
    {
        int l = 1;
        for (const auto r : t[i])
        {
            if (l == r)
            {
                l = r + 1;
                continue;
            }
            node[++q] = Mo(l, r - 1), l = r + 1;
        }
        if (l < n)node[++q] = Mo(l, n);
    }
    sortArr(node, q);
    int l = 1, r = 0, last = 0;
    forn(i, 1, q)
    {
        const auto [L,R] = node[i];
        if (pos[L] == pos[R])
        {
            forn(i, L, R)tmp[a[i]]++;
            int mex = 1;
            while (tmp[mex])mex++;
            vis[mex] = true;
            forn(i, L, R)tmp[a[i]]--;
            continue;
        }
        if (pos[L] != last)
        {
            last = pos[L];
            curr = 1;
            forn(i, 1, n)Cnt[i] = 0;
            forn(i, s[pos[L]], n)Cnt[a[i]]++;
            while (Cnt[curr])curr++;
            l = s[pos[L]], r = n;
        }
        while (r > R)del(a[r--]);
        const int pre = curr;
        int tmpL = l;
        while (tmpL < L)del(a[tmpL++], true);
        vis[curr] = true;
        while (!back.empty())Cnt[back.top()]++, back.pop();
        curr = pre;
    }
    int mex = 1;
    while (vis[mex])mex++;
    cout << mex;
}

signed int main()
{
    // MyFile
    Spider
    //------------------------------------------------------
    // clock_t start = clock();
    int test = 1;
    //    read(test);
    // cin >> test;
    forn(i, 1, test)solve();
    //    while (cin >> n, n)solve();
    //    while (cin >> test)solve();
    // clock_t end = clock();
    // cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

\[时间复杂度为:\ O(n\sqrt{n}) \]

解法三

在线主席树上二分

关于 \(mex\) 和颜色数问题是属于同一条道路的,有一类经典分析,就是 \(pre/last\) 分析。

对于询问区间 \([l,r]\)\(mex\),我们有这样的暴力方法:

枚举 \(1 \sim n\),然后 \(check\) 它是否在这个区间内。这个 \(check\) 它是否在一个区间内显然有很多种方法,我要讲一种很经典的方法,以便于拓展。对于一个数是否在这个区间内只需要看 \(l \le last[x] \le r\),即我们从 \(1\rightarrow r\) 时,\(x\) 最近出现的位置需要在 \([l,r]\) 上,那么这个 \(x\) 就对 \(mex\) 有贡献。而 \(mex\) 显然就为从 \(1 \sim n\) 第一个不满足 \(last[x]\ge l\) 的点。

这个咋解决?我们抽象下:

为每个权值记录 \(last\),对于每个查询 \([l,r]\)

我们维护 \(权值 \Rightarrow last[权值]\),而 \(mex\) 即为 \(1 \sim n\) 上第一个 \(last[i]<l\)\(i\),这个咋做?这不就是经典线段树二分吗?我们咋维护一个区间 \(min\),我们就能找到 \(last[i]<l\) 是哪一半边,就能二分出这个第一个点。完毕。

对于一堆查询,它们的 \(r\) 并不固定,但每次查询的即为 \(1\rightarrow r\) 更新完毕的状态,因为我们要保证 \(last[x]\) 是更新到 \(r\) 的最近的 \(x\) 的位置。这玩意在线做,就是主席树了,查询 \(root[r]\) 处的主席树,就是 \(0 \sim r\) 的状态了。当然也可以树套树直接暴力做,不过复杂度就劣了,我们要求查询的是前缀状态,主席树足矣。

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

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")

// #define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>

#endif

using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

template <typename T>
int disc(T* a, int n)
{
    return unique(a + 1, a + n + 1) - (a + 1);
}

template <typename T>
T lowBit(T x)
{
    return x & -x;
}

template <typename T>
T Rand(T l, T r)
{
    static mt19937 Rand(time(nullptr));
    uniform_int_distribution<T> dis(l, r);
    return dis(Rand);
}

template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
    return (a % b + b) % b;
}

template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
    a %= c;
    T1 ans = 1;
    for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
    return modt(ans, c);
}

template <typename T>
void read(T& x)
{
    x = 0;
    T sign = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    x *= sign;
}

template <typename T, typename... U>
void read(T& x, U&... y)
{
    read(x);
    read(y...);
}

template <typename T>
void write(T x)
{
    if (typeid(x) == typeid(char))return;
    if (x < 0)x = -x, putchar('-');
    if (x > 9)write(x / 10);
    putchar(x % 10 ^ 48);
}

template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
    write(x), putchar(c);
    write(c, y...);
}


template <typename T11, typename T22, typename T33>
struct T3
{
    T11 one;
    T22 tow;
    T33 three;

    bool operator<(const T3 other) const
    {
        if (one == other.one)
        {
            if (tow == other.tow)return three < other.three;
            return tow < other.tow;
        }
        return one < other.one;
    }

    T3() { one = tow = three = 0; }

    T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
    {
    }
};

template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
    if (x < y)x = y;
}

template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
    if (x > y)x = y;
}

constexpr int N = 2e5 + 10;
int n, q;
vector<int> t[N];
int a[N];
bool vis[N];
int cnt;
constexpr int INF = 1e9 + 7;

struct Node
{
    int mi;
    int left, right;
} node[N << 5];

#define left(x) node[x].left
#define right(x) node[x].right
#define mi(x) node[x].mi
int root[N];

inline void pushUp(const int curr)
{
    mi(curr) = min(mi(left(curr)),mi(right(curr)));
}

inline void add(const int pre, int& curr, const int pos, const int val, const int l = 1, const int r = n + 2)
{
    node[curr = ++cnt] = node[pre];
    const int mid = l + r >> 1;
    if (l == r)
    {
        mi(curr) = val;
        return;
    }
    if (pos <= mid)add(left(pre),left(curr), pos, val, l, mid);
    else add(right(pre),right(curr), pos, val, mid + 1, r);
    pushUp(curr);
}

inline int queryMex(const int curr, const int pos, const int l = 1, const int r = n + 2)
{
    if (l == r)return l;
    const int mid = l + r >> 1;
    if (mi(left(curr)) < pos)return queryMex(left(curr), pos, l, mid);
    return queryMex(right(curr), pos, mid + 1, r);
}

inline void solve()
{
    cin >> n;
    forn(i, 1, n)cin >> a[i], t[a[i]].push_back(i);
    forn(i, 1, n)add(root[i - 1], root[i], a[i], i);
    forn(i, 1, n+1)
    {
        int l = 1;
        for (const auto r : t[i])
        {
            if (l == r)
            {
                l = r + 1;
                continue;
            }
            vis[queryMex(root[r - 1], l)] = true, l = r + 1;
        }
        if (l < n)vis[queryMex(root[n], l)] = true;
    }
    int mex = 1;
    while (vis[mex])mex++;
    cout << mex;
}

signed int main()
{
    // MyFile
    Spider
    //------------------------------------------------------
    // clock_t start = clock();
    int test = 1;
    //    read(test);
    // cin >> test;
    forn(i, 1, test)solve();
    //    while (cin >> n, n)solve();
    //    while (cin >> test)solve();
    // clock_t end = clock();
    // cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

\[时间复杂度为:\ O(n\log{n}) \]

第四种解法

离线扫描线询问+线段树上二分

既然本题可以在线做,当然也可以离线做,我们把所有询问挂载在序列扫描线上,直接从 \(1-r\) 更新线段树的同时处理询问即可。

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

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")

// #define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>

#endif

using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

template <typename T>
int disc(T* a, int n)
{
    return unique(a + 1, a + n + 1) - (a + 1);
}

template <typename T>
T lowBit(T x)
{
    return x & -x;
}

template <typename T>
T Rand(T l, T r)
{
    static mt19937 Rand(time(nullptr));
    uniform_int_distribution<T> dis(l, r);
    return dis(Rand);
}

template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
    return (a % b + b) % b;
}

template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
    a %= c;
    T1 ans = 1;
    for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
    return modt(ans, c);
}

template <typename T>
void read(T& x)
{
    x = 0;
    T sign = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    x *= sign;
}

template <typename T, typename... U>
void read(T& x, U&... y)
{
    read(x);
    read(y...);
}

template <typename T>
void write(T x)
{
    if (typeid(x) == typeid(char))return;
    if (x < 0)x = -x, putchar('-');
    if (x > 9)write(x / 10);
    putchar(x % 10 ^ 48);
}

template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
    write(x), putchar(c);
    write(c, y...);
}


template <typename T11, typename T22, typename T33>
struct T3
{
    T11 one;
    T22 tow;
    T33 three;

    bool operator<(const T3 other) const
    {
        if (one == other.one)
        {
            if (tow == other.tow)return three < other.three;
            return tow < other.tow;
        }
        return one < other.one;
    }

    T3() { one = tow = three = 0; }

    T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
    {
    }
};

template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
    if (x < y)x = y;
}

template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
    if (x > y)x = y;
}

constexpr int N = 2e5 + 10;
int n, q;
vector<int> t[N];
int a[N];
bool vis[N];
int cnt;
constexpr int INF = 1e9 + 7;

struct Node
{
    int mi;
} node[N << 2];

#define mi(x) node[x].mi

inline void push_up(const int curr)
{
    mi(curr) = min(mi(ls(curr)),mi(rs(curr)));
}

inline void update(const int curr, const int pos, const int val, const int l = 1, const int r = n + 2)
{
    if (l == r)
    {
        mi(curr) = val;
        return;
    }
    const int mid = l + r >> 1;
    if (pos <= mid)update(ls(curr), pos, val, l, mid);
    else update(rs(curr), pos, val, mid + 1, r);
    push_up(curr);
}

inline int queryMex(const int curr, const int pos, const int l = 1, const int r = n + 2)
{
    if (l == r)return l;
    const int mid = l + r >> 1;
    if (mi(ls(curr)) < pos)return queryMex(ls(curr), pos, l, mid);
    return queryMex(rs(curr), pos, mid + 1, r);
}

vector<int> seg[N];

inline void solve()
{
    cin >> n;
    forn(i, 1, n)cin >> a[i], t[a[i]].push_back(i);
    forn(i, 1, n+1)
    {
        int l = 1;
        for (const auto r : t[i])
        {
            if (l == r)
            {
                l = r + 1;
                continue;
            }
            seg[r - 1].push_back(l), l = r + 1;
        }
        if (l < n)seg[n].push_back(l);
    }
    forn(r, 1, n)
    {
        update(1, a[r], r);
        for (const auto l : seg[r])vis[queryMex(1, l)] = true;
    }
    int mex = 1;
    while (vis[mex])mex++;
    cout << mex;
}

signed int main()
{
    // MyFile
    Spider
    //------------------------------------------------------
    // clock_t start = clock();
    int test = 1;
    //    read(test);
    // cin >> test;
    forn(i, 1, test)solve();
    //    while (cin >> n, n)solve();
    //    while (cin >> test)solve();
    // clock_t end = clock();
    // cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

\[时间复杂度为:\ O(n\log{n}) \]

一些细节

本题注意 \(mex\) 最大为 \(n+1\),所以记得枚举到 \(n+1\)。注意每个包含 \(i\) 的位置是不可取的,我们可以适当的剪枝,对于 \([l,r]\) 我们要求这个区间的 \(mex\)\(i\),如果区间元素数量都不够,那显然不可能形成 \(i\)。最后,当然不用完全枚举 \(1\sim n+1\),一直枚举到 \(mex\) 确定的时候就可以停止枚举了。

剪枝参照
#include <bits/stdc++.h>

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")

// #define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>

#endif

using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

template <typename T>
int disc(T* a, int n)
{
    return unique(a + 1, a + n + 1) - (a + 1);
}

template <typename T>
T lowBit(T x)
{
    return x & -x;
}

template <typename T>
T Rand(T l, T r)
{
    static mt19937 Rand(time(nullptr));
    uniform_int_distribution<T> dis(l, r);
    return dis(Rand);
}

template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
    return (a % b + b) % b;
}

template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
    a %= c;
    T1 ans = 1;
    for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
    return modt(ans, c);
}

template <typename T>
void read(T& x)
{
    x = 0;
    T sign = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    x *= sign;
}

template <typename T, typename... U>
void read(T& x, U&... y)
{
    read(x);
    read(y...);
}

template <typename T>
void write(T x)
{
    if (typeid(x) == typeid(char))return;
    if (x < 0)x = -x, putchar('-');
    if (x > 9)write(x / 10);
    putchar(x % 10 ^ 48);
}

template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
    write(x), putchar(c);
    write(c, y...);
}


template <typename T11, typename T22, typename T33>
struct T3
{
    T11 one;
    T22 tow;
    T33 three;

    bool operator<(const T3 other) const
    {
        if (one == other.one)
        {
            if (tow == other.tow)return three < other.three;
            return tow < other.tow;
        }
        return one < other.one;
    }

    T3() { one = tow = three = 0; }

    T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
    {
    }
};

template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
    if (x < y)x = y;
}

template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
    if (x > y)x = y;
}

constexpr int N = 2e5 + 10;
int n, q;
vector<int> t[N];
int a[N];
bool vis[N];
int cnt;
constexpr int INF = 1e9 + 7;

struct Node
{
    int mi;
} node[N << 2];

#define mi(x) node[x].mi

inline void push_up(const int curr)
{
    mi(curr) = min(mi(ls(curr)),mi(rs(curr)));
}

inline void update(const int curr, const int pos, const int val, const int l = 1, const int r = n + 2)
{
    if (l == r)
    {
        mi(curr) = val;
        return;
    }
    const int mid = l + r >> 1;
    if (pos <= mid)update(ls(curr), pos, val, l, mid);
    else update(rs(curr), pos, val, mid + 1, r);
    push_up(curr);
}

inline int queryMex(const int curr, const int pos, const int l = 1, const int r = n + 2)
{
    if (l == r)return l;
    const int mid = l + r >> 1;
    if (mi(ls(curr)) < pos)return queryMex(ls(curr), pos, l, mid);
    return queryMex(rs(curr), pos, mid + 1, r);
}

vector<int> seg[N];

inline void solve()
{
    cin >> n;
    forn(i, 1, n)cin >> a[i], t[a[i]].push_back(i);
    forn(i, 1, n+1)
    {
        int l = 1;
        bool isAdd = false;
        for (const auto r : t[i])
        {
            if (l == r)
            {
                l = r + 1;
                continue;
            }
            if (r - l >= i - 1)
            {
                isAdd = true;
                seg[r - 1].push_back(l);
            }
            l = r + 1;
        }
        if (l < n)
        {
            if (n - l + 1 >= i - 1)
            {
                isAdd = true;
                seg[n].push_back(l);
            }
        }
        if (!isAdd)break;
    }
    forn(r, 1, n)
    {
        update(1, a[r], r);
        for (const auto l : seg[r])vis[queryMex(1, l)] = true;
    }
    int mex = 1;
    while (vis[mex])mex++;
    cout << mex;
}

signed int main()
{
    // MyFile
    Spider
    //------------------------------------------------------
    // clock_t start = clock();
    int test = 1;
    //    read(test);
    // cin >> test;
    forn(i, 1, test)solve();
    //    while (cin >> n, n)solve();
    //    while (cin >> test)solve();
    // clock_t end = clock();
    // cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

posted @ 2024-03-08 12:39  Athanasy  阅读(54)  评论(0编辑  收藏  举报