一道不错的题目。首先套路明显是逐位贪心。那么现在问题变成了如何判断一种方案是否可行。
定义原前缀最大值为在p中的前缀最大值。考虑这个题目的几个最重要的性质:
那么所以,我们可以得到一些推论:
意义就是考虑对于原前缀最大值,其权值为 2 2 2,反之为 1 1 1,问是否存在一个上升子序列满足其权值等于 l 1 − l 2 + Q l_1-l_2+Q l1−l2+Q,设这个数为 L L L。
首先,如果子序列最大值都 ≤ L \leq L ≤L,那么一定不行。
反之,如果子序列的最大值为奇数,那么一定是可以的。原因是由于最大值是奇数,那么其中一定至少有一个非原前缀最大值,即最大值是如下构成的: 2 , 2 , 2 , 2... , 1 2,2,2,2...,1 2,2,2,2...,1,从而可以构成不大于最大值的任何非负整数。(可以每次减二,减到不能减为止,如果 L L L为偶数,就再减去一个 1 1 1)。
如果最大值为偶数,由于不一定有 1 1 1,所以不一定所有数都能构成。这种情况发生在 L L L是奇数下,但是我们发现如果可行,那么权值为奇数的子序列的最大值一定 ≥ L \geq L ≥L。
综上,实际上只需要判断与 L L L同奇偶权值的子序列的权值最大值即可。
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ph push
#define ptc putchar
#define enter putchar('\n')
#define mod 998244353
using namespace std;
typedef pair<int,int> pii;
typedef double db;
typedef long double ldb;
typedef long long ll;
typedef long long lnt;
inline int read(){
int x = 0;char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = (x << 1) + (x << 3) + c - '0' , c = getchar();
return x;
}
inline void write(int x){
if (!x){
ptc('0');
return;
}
int dg[20] , len = 0;
while (x) dg[len++] = x % 10 , x /= 10;
while (len--) ptc(dg[len]+'0');
}
inline void writeln(int x){
write(x);
ptc('\n');
}
inline int add(int x,int y){
x += y;if (x >= mod) x -= mod;
return x;
}
inline int sub(int x,int y){
x -= y;if (x < 0) x += mod;
return x;
}
inline int qpow(int x,int y){
int res = 1;
while (y){
if (y & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod;y >>= 1;
}
return res;
}
const int N = 2e5 + 10;
int n , a[N] , premax[N] , sufsumpremaxcnt[N];
struct Node{
int maxn , ls , rs;
}tr[N * 50];
int tcnt;
struct Segment_Tree{
int rt[N];
void up(int x){
if (!tr[x].ls) tr[x].maxn = tr[tr[x].rs].maxn;else
if (!tr[x].rs) tr[x].maxn = tr[tr[x].ls].maxn;else
tr[x].maxn = max(tr[tr[x].ls].maxn , tr[tr[x].rs].maxn);
}
void insert(int &x,int prex,int l,int r,int pos,int v){
x = ++tcnt;
if (l == r){
tr[x].maxn = v;
return;
}
int mid = l + r >> 1;
if (pos <= mid) {
tr[x].rs = tr[prex].rs;
insert(tr[x].ls , tr[prex].ls , l , mid , pos , v);
}else{
tr[x].ls = tr[prex].ls;
insert(tr[x].rs , tr[prex].rs , mid + 1 , r , pos , v);
}
up(x);
}
int query(int x,int l,int r,int ql,int qr){
if (!x || ql > qr) return 0;
if (ql <= l && r <= qr) return tr[x].maxn;
int mid = l + r >> 1 , res = 0;
if (ql <= mid) res = max(res , query(tr[x].ls , l , mid , ql , qr));
if (mid < qr ) res = max(res , query(tr[x].rs , mid + 1 , r , ql , qr));
return res;
}
}T[2];
int s[2][N] , maxn[2][N] , len[2] , sum[2];
int res[N];
int check(int id,int pos){//[pos , n]
int need = sum[id] - sum[id ^ 1] + sufsumpremaxcnt[pos];
if (need < 0) return 0;
int most = T[need & 1].query(T[need & 1].rt[pos] , 1 , n , maxn[id ^ 1][len[id ^ 1]] + 1 , n);
return most >= need;
}
int main(){
scanf("%d",&n);
for (int i = 1;i <= n;i++){
scanf("%d",a + i);
premax[i] = max(premax[i-1] , a[i]);
}
for (int i = n;i >= 1;i--){
sufsumpremaxcnt[i] = sufsumpremaxcnt[i + 1] + (premax[i] == a[i]);
}
for (int i = n;i >= 1;i--){
int d0max = T[0].query(T[0].rt[i+1] , 1 , n , a[i] + 1 , n) , d1max = T[1].query(T[1].rt[i+1] , 1 , n , a[i] + 1 , n);
if (premax[i] == a[i]){
T[0].insert(T[0].rt[i] , T[0].rt[i+1] , 1 , n , a[i] , d0max + 2);
if (d1max) T[1].insert(T[1].rt[i] , T[1].rt[i + 1] , 1 , n , a[i] , d1max + 2);else T[1].rt[i] = T[1].rt[i + 1];
}else{
T[1].insert(T[1].rt[i] , T[1].rt[i+1] , 1 , n , a[i] , d0max + 1);
if (d1max) T[0].insert(T[0].rt[i] , T[0].rt[i+1] , 1 , n , a[i] , d1max + 1);else T[0].rt[i] = T[0].rt[i + 1];
}
}
memset(res , -1 , sizeof res);
for (int i = 1;i <= n;i++){
for (int d = 0;d < 2;d++){
s[d][++len[d]] = a[i];
maxn[d][len[d]] = max(maxn[d][len[d] - 1] , a[i]);
sum[d] += maxn[d][len[d]] == s[d][len[d]];
if (check(0 , i + 1)) {res[i] = d;break;}
if (check(1 , i + 1)) {res[i] = d;break;}
sum[d] -= maxn[d][len[d]] == s[d][len[d]];
--len[d];
}
if (res[i] == -1) return puts("-1") , 0;
}
for (int i = 1;i <= n;i++) printf("%d",res[i]);
return 0;
}