AGC028 E - High Elements

AGC028 E - High Elements


有一个排列\(p\),你要分成两个子序列\(A,B\),满足\(A,B\)的LIS长度相等。设\(S\)是一个\(01\)序列,\(S_i=0\)当且仅当\(i\)被分到\(A\)中。求满足条件的字典序最小的\(S\)

显然算法大约为贪心考虑每一位是否可以是\(0\)

有一个结论:

\(p\)上升点是\(p\)中lis中的点,上升点是指在这个子序列lis中的点,上升点但不是\(p\)上升点称为新上升点。

  1. 新上升点是上升点是因为它前面的大于它的点都分到了另一个子序列。所以如果将新上升点放到另一个子序列就不是新上升点了

  2. 一定可以做到有一个序列中没有新上升点,如果不满足,考虑\(A,B\)各有一个新上升点,把它们放到另外一个子序列中,就都少了一个上升点,数量还是相等。钦定这个没有新上升点的子序列是\(A\)

考虑\([i+1,n]\)是否可以盒子安排使得\(A,B\)LIS长度相等。设\([1,i]\)\(A\)上升点数量为\(la\)\(B\)的为\(lb\)\([i+1,n]\)中有\(k\)\(p\)上升点。

如果\(k\)\(p\)上升点\(B\)分了\(pb\)个,\(A\)分了\(k-pb\)个,而且\(B\)中还有\(s\)个新上升点,那么可以根据\(A,B\)上升点相同列式子:

\[la+k-pb=lb+pb+s \]

\[la+k-lb=2pb+s \]

左边是常数,设为\(c\)。现在只需要知道能不能在\([i+1,n]\)中选一个上升子序列权值和为\(c\)\(p\)上升点权值为\(2\),新上升点为\(1\)

这个直接线段树一下就行了。

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il ll gi(){
	ll x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch))f^=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
int n,p[200010];
int f[2][200010],ismx[200010];
struct segtree{
#define mid ((l+r)>>1)
	int rt[200010],ls[6000010],rs[6000010],mx[6000010],cnt;
	il vd update(int&x,int l,int r,const int&p,const int&d){
		++cnt;ls[cnt]=ls[x],rs[cnt]=rs[x],mx[cnt]=mx[x];x=cnt;
		if(l==r){mx[x]=d;return;}
		if(d>mx[x])mx[x]=d;
		if(p<=mid)update(ls[x],l,mid,p,d);
		else update(rs[x],mid+1,r,p,d);
	}
	il int query(int x,int l,int r,const int&L){
		if(L<=l)return mx[x];
		if(L<=mid)return std::max(query(ls[x],l,mid,L),query(rs[x],mid+1,r,L));
		else return query(rs[x],mid+1,r,L);
	}
#undef mid
}T0,T1;
char ans1[200010],ans2[200010];
il int Query(int c,int l,int p){
	if(l>n)return c?-1e9:0;
	if(!c)return T0.query(T0.rt[l],1,n,p);
	else return T1.query(T1.rt[l],1,n,p);
}
int main(){
#ifdef XZZSB
	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);
#endif
	n=gi();
	for(int i=1;i<=n;++i)p[i]=gi();
	int la1=0,lb1=0,la2=0,lb2=0,mxa1=0,mxa2=0,mxb1=0,mxb2=0,rest=0;
	for(int i=1,mx=0;i<=n;++i)if(p[i]>mx)mx=p[i],ismx[i]=1,++rest;
	memset(T1.mx,-63,sizeof T1.mx);
	for(int i=n;i;--i){
		T0.rt[i]=T0.rt[i+1];T1.rt[i]=T1.rt[i+1];
		if(ismx[i])f[0][i]=T0.query(T0.rt[i],1,n,p[i])+2,f[1][i]=T1.query(T1.rt[i],1,n,p[i])+2;
		else f[0][i]=T1.query(T1.rt[i],1,n,p[i])+1,f[1][i]=T0.query(T0.rt[i],1,n,p[i])+1;
		T0.update(T0.rt[i],1,n,p[i],f[0][i]);T1.update(T1.rt[i],1,n,p[i],f[1][i]);
	}
	auto adda1=[&](int x){if(x>mxa1)++la1,mxa1=x;};
	auto adda2=[&](int x){if(x>mxa2)++la2,mxa2=x;};
	auto addb1=[&](int x){if(x>mxb1)++lb1,mxb1=x;};
	auto addb2=[&](int x){if(x>mxb2)++lb2,mxb2=x;};
	for(int i=1;i<=n;++i){
		int c=la1+rest-lb1+(p[i]>mxa1)-ismx[i];
		if(c>=0&&Query(c&1,i+1,mxb1)>=c)ans1[i]='0',adda1(p[i]);
		else ans1[i]='1',addb1(p[i]);
		c=la2+rest-lb2-(p[i]>mxb2)-ismx[i];
		if(c>=0&&Query(c&1,i+1,std::max(p[i],mxb2))>=c)ans2[i]='0',addb2(p[i]);
		else ans2[i]='1',adda2(p[i]);
		if(ismx[i])--rest;
	}
	if(la1==lb1&&la2==lb2){
		if(strcmp(ans1+1,ans2+1)<0)printf("%s",ans1+1);
		else printf("%s",ans2+1);
	}else if(la1==lb1)printf("%s",ans1+1);
	else if(la2==lb2)printf("%s",ans2+1);
	else puts("-1");
	return 0;
}
posted @ 2019-07-07 15:57  菜狗xzz  阅读(341)  评论(1编辑  收藏  举报