AGC028E
题意
给 \(n\) 和排列 \(a_1,\ a_2,\ ...,\ a_n\)。令 \(b_1,\ b_2,\ ...,\ b_n,\ b_i\ \in\ \{0,\ 1\},\ c\ =\ \{\ a_i\ |\ b_i\ =\ 0\},\ d\ =\ \{\ a_i\ |\ b_i\ =\ 1\}\),其中 \(c\) 和 \(d\) 数字顺序与 \(a\) 中顺序相同。称 \(b\) 是好的当且仅当 \(c\) 和 \(d\) 的前缀最大值个数相同。
问字典序最小的好的 \(b\)。若不存在输出 \(-1\)。
\(1\ \leq\ n\ \leq\ 10^5\)
做法1
由于字典序最小,考虑逐位确定。设当前已经填好了 \(i\) 位,\(c\) 的最大值为 \(max_0\),\(c\) 的前缀最大值个数为 \(cnt_0\),\(d\) 的最大值为 \(max_1\),\(d\) 的前缀最大值个数为 \(cnt_1\)。则当前填的合法当且仅当存在如下数列 \(p\) 和 \(q\):
- \(max_0\ <\ p_1\ <\ p_2\ <\ ...\ <\ p_k\)
- \(max_1\ <\ q_1\ <\ q_2\ <\ ...\ <\ q_m\)
- \(cnt_0\ +\ k\ =\ cnt_1\ +\ m\)
- \(a\) 中前缀最大值在 \(i\) 之后的出现在 \(p\) 或 \(q\) 中。
现若 \(p\) 和 \(q\) 中均出现 \(a\) 中非前缀最大值的数,则删去这两个数对合法性无影响。所以可以枚举哪一个序列只能由 \(a\) 中的前缀最大值构成,然后判断。可以发现 \(k\ -\ m\) 的取值为 \([l_0,\ r_0]\) 中的偶数和 \([l_1,\ r_1]\) 中的奇数,\(r_0,\ r_1\) 值会在另一个序列取了 \(lis\) 时取到。
时间复杂度 \(O(n\ log\ n)\)
代码
#include <bits/stdc++.h>
#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif
using namespace std;
struct segment_tree {
segment_tree() {}
segment_tree(int _n) {
n = _n;
N = 1; while(N - 1 <= n) N <<= 1;
a = vector<int>(N << 1, 0);
}
void M(int i, int x) {
a[i |= N] = x;
for (i >>= 1; i; i >>= 1) a[i] = max(a[i << 1], a[(i << 1) | 1]);
return;
}
int Q(int i) {
int x = 0;
for (int s = (i | N) - 1, t = (n | N) + 1; s ^ t ^ 1; s >>= 1, t >>= 1) {
if(~s & 1) x = max(x, a[s ^ 1]);
if(t & 1) x = max(x, a[t ^ 1]);
}
return x;
}
private :
vector<int> a;
int n, N;
};
int main() {
ios::sync_with_stdio(false);
int n; cin >> n;
vector<int> a(n), s(n, 0);
vector<bool> h(n);
for (int mx = 0, i = 0; i < n; ++i) cin >> a[i], h[i] = (a[i] > mx), mx = max(mx, a[i]);
for (int i = n - 1; i; --i) s[i - 1] = s[i] + h[i];
array<segment_tree, 2> seg = {segment_tree(n), segment_tree(n)};
for (int i = n - 1; ~i; --i) {
array<int, 2> g = {seg[0].Q(a[i]), seg[1].Q(a[i])};
if(h[i]) {
seg[0].M(a[i], g[0] + 2);
if(g[1] & 1) seg[1].M(a[i], g[1] + 2);
}
else {
seg[1].M(a[i], g[0] + 1);
if(g[1] & 1) seg[0].M(a[i], g[1] + 1);
}
}
int max0(0), max1(0), cnt0(0), cnt1(0);
string ans("");
for (int x, i = 0; i < n; ++i) {
x = a[i];
seg[0].M(x, 0); seg[1].M(x, 0);
auto check = [&](int max0, int cnt0, int max1, int cnt1) {
static array<int, 2> g;
g = {seg[0].Q(max0), seg[1].Q(max0)};
if(cnt0 + cnt1 + s[i] & 1) {
if(g[1] & 1) {
if(cnt0 + 1 <= cnt1 + s[i] && cnt0 + g[1] >= cnt1 + s[i]) return true;
}
}
else {
if(cnt0 <= cnt1 + s[i] && cnt0 + g[0] >= cnt1 + s[i]) return true;
}
g = {seg[0].Q(max1), seg[1].Q(max1)};
if(cnt0 + cnt1 + s[i] & 1) {
if(g[1] & 1) {
if(cnt1 + 1 <= cnt0 + s[i] && cnt1 + g[1] >= cnt0 + s[i]) return true;
}
}
else {
if(cnt1 <= cnt0 + s[i] && cnt1 + g[0] >= cnt0 + s[i]) return true;
}
return false;
};
if(check(max(max0, x), cnt0 + (x > max0), max1, cnt1)) { ans += "0"; max0 = max(max0, x); cnt0 += (x == max0); }
else { ans += "1"; max1 = max(max1, x); cnt1 += (x == max1); }
}
if(cnt0 == cnt1) cout << ans << endl;
else cout << "-1\n";
return 0;
}