AGC028E High Elements 做题记录

给定一个 nn 的排列 PP,一个长 nn 的 01 串 SS 合法当且仅当:

  • AA 为所有 Si=1S_i=1aia_i 构成的子序列,BB 为所有 Si=0S_i=0aia_i 构成的子序列,则 AABB 的前缀最大值个数相等;

求所有合法的 SS 中字典序最小的那个。

1n2×1051\le n\le 2\times 10^5

显然 PP 中的前缀最大值在 AABB 中还是前缀最大值,那么不妨将在 PP 中就是前缀最大值的数称为“旧的”,在 PP 中不是但在 AABB 中是前缀最大值的数成为“新的”。

那么有个结论:

若合法的 SS 存在,那么一定存在某个合法的 SS 满足 AA 中的前缀最大值都是旧的。

证明

AABB 中都存在新的前缀最大值,那么交换它们管辖(AAii 能管辖到 jj 当且仅当 Ai=maxk=1jAkA_i=\max\limits_{k=1}^jA_kBB 中同理)到的所有数即可。

那么考虑贪心,从前往后枚举 SS 的每个位,判断这一位填 00 后还存不存在合法的 SS 即可。

设当前填完了 S[1,i]S_{[1,i]}AA 共有 cxcx 个前缀最大值,当前最大值为 mxmxBB 共有 cycy 个前缀最大值,当前最大值为 mymyP[i+1,n]P_{[i+1,n]} 中一共有 cc 个旧的前缀最大值。

不妨假设 AA 之后的前缀最大值全都是旧的,那么设 BB 后面共有 kk 个旧的前缀最大值和 mm 个新的前缀最大值,则当前填法合法当且仅当有:

cx+ck=cy+k+mm+2k=cx+ccy\begin{aligned} cx+c-k&=cy+k+m\\ m+2k&=cx+c-cy \end{aligned}

cx+ccycx+c-cy 是已知的,那么只要判断 m+2km+2k 是否能满足即可。

而这就相当于给旧的前缀最大值赋一个 22 的权值,其它数赋 11,求能否在 P[i+1,n]P_{[i+1,n]} 中找到一个严格上升子序列 RR 满足 R1>myR_1>myRR 的权值和为 cx+ccycx+c-cy

不难发现若找到了权值和为 xx 的严格上升子序列,则一定能找到权值和为 x2x-2 的。所以找到权值和为奇数/偶数的最大的权值和即可。

那么使用值域线段树维护,先倒着扫一遍求出 fi,0/1f_{i,0/1} 表示以 ii 开头的权值和为偶数/奇数的严格上升子序列的最大的权值和,在贪心的时候每次把 fPif_{P_i} 删掉即可。

时间复杂度 O(nlogn)O(n\log n),代码如下:

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