agc028E High Elements

30 篇文章 0 订阅
17 篇文章 0 订阅

agc028E High Elements

  • 将一个长度为 n n n的排列 P i P_i Pi,将它分成两个序列 A A A B B B,使得 A , B A,B A,B的前缀最大值个数相同。
  • s i = 0 s_i=0 si=0表示 i i i被分到 A A A里,反之 s i = 1 s_i=1 si=1分到 B B B里,求一个字典序最小的 s s s满足上面的条件。
  • n ≤ 2 e 5 n\le2e5 n2e5

Solution

  • 首先原排列的前缀最大值一定不管在哪个序列里都是前缀最大值。

  • 我们将原本不是前缀最大值,在分到某个序列之后变成了前缀最大值的元素叫做**“新最大值”**。

  • 那么我们可以发现,对于 A , B A,B A,B的两个新最大值,它们前面最近的**“原最大值”**,一定在另一个序列,所以我们如果交换这两个新最大值, A , B A,B A,B的个数都会减一,也就是说它们的差不会改变。

  • 因此,如果存在解,那么一定存在 A , B A,B A,B中的有一个的所有前缀最大值都是原最大值,因为我们可以从一个解中不断同时删去两个序列的新最大值,直到满足以上的情况。

  • 有了上面的东西我们就不难判定了。

  • 首先按位贪心,然后判定,当前填完了 i i i位,考虑 A , B A,B A,B前缀最大值个数分别为 l a , l b la,lb la,lb,当前最大值分别为 m a , m b ma,mb ma,mb [ i + 1 , n ] [i+1,n] [i+1,n]中的前缀最大值有 Q Q Q个,不妨设 A A A [ i + 1 , n ] [i+1,n] [i+1,n]中的前缀最大值都为原最大值, B B B中有 k k k个原最大值,那么 A A A Q − k Q-k Qk个原最大值,设 B B B中还有 m m m个新最大值。

  • l a + Q − k = l b + k + m la+Q-k=lb+k+m la+Qk=lb+k+m

  • l a − l b + Q = 2 k + m la-lb+Q=2k+m lalb+Q=2k+m

  • 考虑右边如果 x x x可以达到 x − 2 x-2 x2也可以达到,所以维护奇数和偶数的最大值。

  • 后往前用线段树维护某个值为开头的上述最大值,前往后边查询边撤销(覆盖)即可。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 200005
using namespace std;

int n,i,j,k,a[maxn];

struct segmenttree{
	int t[maxn*4],inf;
	void maketree(int x,int l,int r){
		t[x]=inf; if (l==r) return;
		int mid=(l+r)>>1;
		maketree(x<<1,l,mid),maketree(x<<1^1,mid+1,r);
	}
	int find(int x,int l,int r,int L,int R){
		if (l>R||r<L) return inf;
		if (L<=l&&r<=R) return t[x];
		int mid=(l+r)>>1;
		return max(find(x<<1,l,mid,L,R),find(x<<1^1,mid+1,r,L,R));
	}
	void change(int x,int l,int r,int p,int v){
		if (l==r) {t[x]=max(v,inf);return;}
		int mid=(l+r)>>1;
		if (p<=mid) change(x<<1,l,mid,p,v);
		else change(x<<1^1,mid+1,r,p,v);
		t[x]=max(t[x<<1],t[x<<1^1]);
	}
} t[2];

int bz[maxn],Q[maxn];
void prepare(){
	t[0].inf=0,t[1].inf=-1e9;
	t[0].maketree(1,1,n),t[1].maketree(1,1,n);
	int mx=0;
	for(i=1;i<=n;i++) mx=max(mx,a[i]),bz[i]=(a[i]==mx)+1;
	for(i=n;i>=1;i--) Q[i]=Q[i+1]+bz[i]-1;
	for(i=n;i>=1;i--) for(j=0;j<2;j++)
		t[j^(bz[i]&1)].change(1,1,n,a[i],t[j].find(1,1,n,a[i]+1,n)+bz[i]);
}

void clear(int i){
	t[0].change(1,1,n,a[i],t[0].inf);
	t[1].change(1,1,n,a[i],t[1].inf);
}

int s[maxn],lena,lenb,mxa,mxb;
int check(int i,int la,int lb,int ma,int mb){
	if (la-lb+Q[i]>=0&&t[(la-lb+Q[i])&1].find(1,1,n,mb+1,n)>=la-lb+Q[i])
		return 1;
	if (lb-la+Q[i]>=0&&t[(lb-la+Q[i])&1].find(1,1,n,ma+1,n)>=lb-la+Q[i])	
		return 1;
	return 0;
}

void doit(){
	lena=lenb=mxa=mxb=0;
	clear(1);
	if (!check(1,lena,lenb,mxa,mxb))
		printf("-1"),exit(0);
	for(i=1;i<=n;i++){
		if (check(i+1,lena+(a[i]>mxa),lenb,max(mxa,a[i]),mxb))
			s[i]=0,lena+=a[i]>mxa,mxa=max(mxa,a[i]); else 
			s[i]=1,lenb+=a[i]>mxb,mxb=max(mxb,a[i]);
		if (i<n) clear(i+1);
	}
}

int main(){
	freopen("ceshi.in","r",stdin);
	scanf("%d",&n);
	for(i=1;i<=n;i++) scanf("%d",&a[i]);
	prepare();
	doit();
	for(i=1;i<=n;i++) putchar('0'+s[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值