给定一个 的排列 ,一个长 的 01 串 合法当且仅当:
- 设 为所有 的 构成的子序列, 为所有 的 构成的子序列,则 和 的前缀最大值个数相等;
求所有合法的 中字典序最小的那个。
。
显然 中的前缀最大值在 和 中还是前缀最大值,那么不妨将在 中就是前缀最大值的数称为“旧的”,在 中不是但在 或 中是前缀最大值的数成为“新的”。
那么有个结论:
若合法的 存在,那么一定存在某个合法的 满足 中的前缀最大值都是旧的。
证明
若 和 中都存在新的前缀最大值,那么交换它们管辖( 中 能管辖到 当且仅当 , 中同理)到的所有数即可。
那么考虑贪心,从前往后枚举 的每个位,判断这一位填 后还存不存在合法的 即可。
设当前填完了 , 共有 个前缀最大值,当前最大值为 ; 共有 个前缀最大值,当前最大值为 , 中一共有 个旧的前缀最大值。
不妨假设 之后的前缀最大值全都是旧的,那么设 后面共有 个旧的前缀最大值和 个新的前缀最大值,则当前填法合法当且仅当有:
是已知的,那么只要判断 是否能满足即可。
而这就相当于给旧的前缀最大值赋一个 的权值,其它数赋 ,求能否在 中找到一个严格上升子序列 满足 且 的权值和为 。
不难发现若找到了权值和为 的严格上升子序列,则一定能找到权值和为 的。所以找到权值和为奇数/偶数的最大的权值和即可。
那么使用值域线段树维护,先倒着扫一遍求出 表示以 开头的权值和为偶数/奇数的严格上升子序列的最大的权值和,在贪心的时候每次把 删掉即可。
时间复杂度 ,代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=200005;
struct segment
{
	int mx[S<<2];
	inline void upda(int u)
	{
		mx[u]=max(mx[u<<1],mx[u<<1|1]);
	}
	void upd(int u,int l,int r,int p,int x)
	{
		if(l==r) return mx[u]=x,void();
		int mid=l+r>>1;
		if(p<=mid) upd(u<<1,l,mid,p,x);
		else upd(u<<1|1,mid+1,r,p,x);
		upda(u);
	}
	int que(int u,int l,int r,int L,int R)
	{
		if(l>R||r<L) return -1e8;
		if(l>=L&&r<=R) return mx[u];
		int mid=l+r>>1,res=-1e8;
		if(L<=mid) res=max(res,que(u<<1,l,mid,L,R));
		if(R>=mid+1) res=max(res,que(u<<1|1,mid+1,r,L,R));
		return res;
	}
}tr[2];
int n,a[S];
bool flg[S];
int ans[S];
inline bool chk(int x,int val)
{
	if(val<0) return false;
	return tr[val&1].que(1,1,n,x,n)>=val;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1,mx=0;i<=n;i++)
	{
		flg[i]=a[i]>mx;
		mx=max(mx,a[i]);
	}
	for(int i=1;i<=n;i++) tr[1].upd(1,1,n,i,-1e8);
	for(int i=n;i>=1;i--)
	{
		int p0=tr[0].que(1,1,n,a[i],n);
		int p1=tr[1].que(1,1,n,a[i],n);
		if(flg[i])
		{
			tr[0].upd(1,1,n,a[i],p0+2);
			tr[1].upd(1,1,n,a[i],p1+2);
		}
		else
		{
			tr[0].upd(1,1,n,a[i],p1+1);
			tr[1].upd(1,1,n,a[i],p0+1);
		}
	}
	int c=0;
	for(int i=1;i<=n;i++) c+=flg[i];
	int cx=0,cy=0,mx=0,my=0;
	for(int i=1;i<=n;i++)
	{
		c-=flg[i];
		tr[0].upd(1,1,n,a[i],0);
		tr[1].upd(1,1,n,a[i],-1e8);
		int nx=cx+(a[i]>mx);
		int valx=nx+c-cy;
		int valy=cy+c-nx;
		if(chk(my,valx)||chk(max(mx,a[i]),valy))
		{
			ans[i]=0;
			cx+=a[i]>mx;
			mx=max(mx,a[i]);
		}
		else
		{
			ans[i]=1;
			cy+=a[i]>my;
			my=max(my,a[i]);
		}
	}
	if(cx!=cy) return puts("-1"),0;
	for(int i=1;i<=n;i++) putchar(ans[i]+'0');
	printf("\n");
	return 0;
}
