首页 > 其他分享 >CF1436E Complicated Computations 题解

CF1436E Complicated Computations 题解

时间:2024-03-08 12:58:06浏览次数:32  
标签:typedef Computations 题解 int Complicated template include mex define

题目链接: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;
}#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\) 确定的时候就可以停止枚举了。

标签:typedef,Computations,题解,int,Complicated,template,include,mex,define
From: https://www.cnblogs.com/Athanasy/p/18060748

相关文章

  • CF935D Fafa and Ancient Alphabet 题解
    讲一个很暴力的方法(为描述方便,下文\(a\)数组代表\(s1\),\(b\)数组代表\(s2\))。发现假如当前\(a_i\neb_i\),就不需要再向下枚举了,于是拥有了分类讨论的雏形。我们设\(inv\)代表进行到这一步的概率,可分为以下四种情况:\(a_i>0,b_i>0\)。此时假如\(a_i=b_i\),略过;若\(a_i>......
  • [HDU6647] Bracket Sequences on Tree 题解
    [HDU6647]BracketSequencesonTree题解一道纯靠自己推出来的换根\(dp+\)树哈希,写篇题解庆祝一下~~题意:给定一棵无根树,你可以任意选择根节点和遍历顺序,每次遍历时进入一个节点就标记一个(,离开一个节点就标记一个),问所有存在的括号序列有多少种,对998244353取模。先考虑根固......
  • CF contest 1935 Round 932 (Div. 2) A-D题解
    CodeforcesRound932(Div.2)A-D题解CodeforcesRound932(Div.2)绪言很菜,AB速度慢,卡在C,想DP,但是时间优化不下来,说服自己\(2\times10^3\)能过\(n^3\),D稍微简单,但是没看D,遂掉分。A.EntertainmentinMAC给你一个字符串\(s\)和一个偶整数\(n\)。你可以对它进行两种运......
  • 【教程】 iOS构建版本无效问题解决方案
     引言在进行iOS应用上架时,有时会遇到构建版本无效的问题,即通过XCode上传成功后,但在AppStoreConnect的TestFlight中无法显示构建版本,或者显示一会儿后就消失了。本文将介绍可能的原因分析,并提供解决问题的方法。问题描述最近一次上传新版本至AppStore后,发现在AppStoreCon......
  • [ARC157F] XY Ladder LCS 题解
    我们尝试给这个抽象题来一篇题解。思考过程还是很重要的。首先看了这个题,一看数据范围\(n\le50\),然后就不懂了,你告诉我这玩意可以状压??然后我们一顿乱想,发现如果\(n\)除以一个\(3\),那我们是不是就可以状压了。那怎么除以\(3\)呢。接着我们手玩一下样例,发现似乎这个答案......
  • [青少年CTF训练平台]web部分题解(已完结!)
    文章管理系统首先打开环境(>ω<。人)ZZz♪♪既然要做题,就要做全面了,图上说了,既然有假flag我就先找出来:假flag:打开vmware,使用sqlmap进行处理:sqlmap-uhttp://challenge.qsnctf.com:31645/?id=1--dbs记得中间的url换成自己的看到了六个可能:{*]ctftraining[*]information......
  • CF1353E K-periodic Garland 题解
    分析考虑DP。定义状态函数\(f_i\)表示处理完前\(i\)个字符且第\(i\)个字符为\(1\)时的最小代价。则对于\(i\),有两种情况:\(i\)不是第一个\(1\),则上一个\(1\)的位置必定为\(i-k\)。\(i\)是第一个\(1\),没有上一个\(1\)。得到转移方程:\(f_i=\min(f_{\max(......
  • P10120『STA - R4』冰红茶 题解
    分析出得很好,模板套模板,希望下次再来。难点在于维护最后连续喝的DS饮料数量。设这次喝原味饮料的区间为\([l,r]\),上一次为\([l',r']\)。则有两种情况:\([l,r]\)与\([l',r']\)不相交。如:在\([l',r']\)和\([l,r]\)两个区间中的DS连续喝的同种饮料数量都会变成\(k......
  • AT_abc343_f [ABC343F] Second Largest Query 题解
    分析考虑乱搞。对于求次大值,用线段树维护就行了。记录下每个区间的最大、次大值。则两个子区间的父区间的最大值就是这四个最大的,次大值就是这四个次大的。复杂度\(O(\logn)\)。求次大值的出现次数,乱搞就行了。因为带修,带修莫队或者分块有些麻烦。其实用线段树就行。在维护区......
  • P8058 [BalkanOI2003] Farey 序列 题解
    分析考虑二分答案。对于当前二分的答案\(x\),设\(cnt\)表示Farey序列中\(\frac{p}{q}\lex\)的满足条件的数量。对于一组\((i,j)\),若\(\frac{j}{i}\lex\),则\(j\le\lfloori\timesx\rfloor\)。得到暴力式子:\[cnt=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{\lfloo......