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,则 mexi,这个很显而易见。

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

如果构造出 mex=i 的可能方式?我们找到所以 aj=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(logV) 插入和删除,O(logV) 查询。查询的话这里要说下如何使用线段树或者树状数组做到单 logV 的复杂度查询,我们只需要维护一个区间最小值,然后二分答案,找第一个 cnt=0 的点,即为找最小值 =0 的第一个点即可。

所以上述复杂度并不合理,我们需要 O(1) 修改,查询可以要求劣一点,O(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(nn)

解法二

回滚莫队

太经典了,区间 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(nn)

解法三

在线主席树上二分

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

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

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

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

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

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

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

参照代码
#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(nlogn)

第四种解法

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

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

参照代码
#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(nlogn)

一些细节

本题注意 mex 最大为 n+1,所以记得枚举到 n+1。注意每个包含 i 的位置是不可取的,我们可以适当的剪枝,对于 [l,r] 我们要求这个区间的 mexi,如果区间元素数量都不够,那显然不可能形成 i。最后,当然不用完全枚举 1n+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 @   Athanasy  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
点击右上角即可分享
微信分享提示