AGC028E High Elements

Solution

一道不错的题目。首先套路明显是逐位贪心。那么现在问题变成了如何判断一种方案是否可行。
定义原前缀最大值为在p中的前缀最大值。考虑这个题目的几个最重要的性质:

  1. 原前缀最大值一定任然是前缀最大值。
  2. 非原前缀最大值之所以能够成为新的前缀最大值,一定是因为他的“克星”在另一个序列中,也就是说,如果将这个数换到另一个序列中,那么他将不会成为前缀最大值。

那么所以,我们可以得到一些推论:

  1. 如果存在解,那么可以使得最后某一个序列中的前缀最大值都是原前缀最大值。原因很简单,因为如果两个序列中都有非原前缀最大值,那么根据性质2,可以交换这两个值所在的序列,这两个值就都变成非前缀最大值,并且两边前缀最大值的数量两边同时减一,任然合法。那么不妨设这个都是原前缀最大值的序列为 A A A,反之为 B B B
  2. 任意取一个上升子序列作为 B B B之后的前缀最大值序列(只要满足这个上升子序列的第一项 ≥ B \geq B B当前的前缀最大值),都是合法的。原因是 B B B先取这些选的数,然后对于剩下的数,我们根据性质2可知它在某一个序列中一定不是前缀最大值,这样就可以控制不是硬点的前缀最大值的值变成一个新的前缀最大。
    现在考虑如果现在 A A A的前缀最大值个数为 l 1 l_1 l1 B B B的为 l 2 l_2 l2。设之后的原前缀最大值有 Q Q Q个,其中 B B B用了 k k k个,同时新增了 m m m个。那么有: l 1 + Q − k = k + m + l 2 l_1 + Q - k = k + m + l_2 l1+Qk=k+m+l2
    l 1 − l 2 + Q = 2 k + m l_1 - l_2 + Q = 2k + m l1l2+Q=2k+m

意义就是考虑对于原前缀最大值,其权值为 2 2 2,反之为 1 1 1,问是否存在一个上升子序列满足其权值等于 l 1 − l 2 + Q l_1-l_2+Q l1l2+Q,设这个数为 L L L
首先,如果子序列最大值都 ≤ L \leq L L,那么一定不行。
反之,如果子序列的最大值为奇数,那么一定是可以的。原因是由于最大值是奇数,那么其中一定至少有一个非原前缀最大值,即最大值是如下构成的: 2 , 2 , 2 , 2... , 1 2,2,2,2...,1 2222...1,从而可以构成不大于最大值的任何非负整数。(可以每次减二,减到不能减为止,如果 L L L为偶数,就再减去一个 1 1 1)。
如果最大值为偶数,由于不一定有 1 1 1,所以不一定所有数都能构成。这种情况发生在 L L L是奇数下,但是我们发现如果可行,那么权值为奇数的子序列的最大值一定 ≥ L \geq L L
综上,实际上只需要判断与 L L L同奇偶权值的子序列的权值最大值即可。

Code

#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;
}


你可能感兴趣的:(atcoder)