题目链接:CF 或者 洛谷
关于 \(mex\) 问题是一个比较久远的问题,有很多经典的方法去解决。本题的 \(mex\) 是从正整数开始的,不要忽略掉。
来讲讲常见的两种解决方案,首先回到题目所问,如果我们暴力地询问:
\(1,2,3,4,.....mex\) 是否都能由原数组构造出来,对于 \(i\) 如果它可以由原数组构造出来,那么即有某个子数组 \(mex(l,r)=i\),那么首先根据 \(mex\) 的定义,我们可以知道 \([l,r]\) 上一定不能含有 \(i\),其次,在这个基础上,\([l,r]\) 尽量大,才也有可能保证 \(mex=i\),具体的你需要知道 \(mex=i\) 的子数组的基本性质:
-
\(mex=i\),则该序列不包括 \(i\),否则可以拓展为 \(mex=i+1\)。
-
\(mex\) 关于 \(len\) 单调不降,即元素个数越多,\(mex\) 越大。
-
\(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;
}
解法二
回滚莫队
太经典了,区间 \(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;
}
解法三
在线主席树上二分
关于 \(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;
}
第四种解法
离线扫描线询问+线段树上二分
既然本题可以在线做,当然也可以离线做,我们把所有询问挂载在序列扫描线上,直接从 \(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;
}
一些细节
本题注意 \(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