CF 277 Div2 E LIS of Sequence

E. LIS of Sequence
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

The next "Data Structures and Algorithms" lesson will be about Longest Increasing Subsequence (LIS for short) of a sequence. For better understanding, Nam decided to learn it a few days before the lesson.

Nam created a sequence a consisting of n (1 ≤ n ≤ 105) elementsa1, a2, ..., an (1 ≤ ai ≤ 105). A subsequence ai1, ai2, ..., aik where 1 ≤ i1 < i2 < ... < ik ≤ n is called increasing ifai1 < ai2 < ai3 < ... < aik. An increasing subsequence is called longest if it has maximum length among all increasing subsequences.

Nam realizes that a sequence may have several longest increasing subsequences. Hence, he divides all indexesi (1 ≤ i ≤ n), into three groups:

  1. group of all i such that ai belongs to no longest increasing subsequences.
  2. group of all i such that ai belongs to at least onebut not every longest increasing subsequence.
  3. group of all i such that ai belongs to every longest increasing subsequence.

Since the number of longest increasing subsequences of a may be very large, categorizing process is very difficult. Your task is to help him finish this job.

Input

The first line contains the single integer n (1 ≤ n ≤ 105) denoting the number of elements of sequencea.

The second line contains n space-separated integersa1, a2, ..., an (1 ≤ ai ≤ 105).

Output

Print a string consisting of n characters.i-th character should be '1', '2' or '3' depending on which group among listed above indexi belongs to.

Sample test(s)
Input
1
4
Output
3
Input
4
1 3 2 5
Output
3223
Input
4
1 5 2 3
Output
3133
Note

In the second sample, sequence a consists of 4 elements:{a1, a2, a3, a4} ={1, 3, 2, 5}. Sequence a has exactly 2 longest increasing subsequences of length 3, they are{a1, a2, a4} ={1, 3, 5} and {a1, a3, a4} ={1, 2, 5}.

In the third sample, sequence a consists of 4 elements:{a1, a2, a3, a4} ={1, 5, 2, 3}. Sequence a have exactly 1 longest increasing subsequence of length 3, that is{a1, a3, a4} ={1, 2, 3}.


解析

LIS问题,先从前往后跑一个最长上升子序列L,再从后往前跑一个最长下降子序列R。其实从前往后看两者都是上升的,注意L[i]<>R[i]。

对于第一类始终不在LIS中的,一定有L[i]+R[i]-1<maxl(其中maxl是最长上升子序列)。

除开第一类看,L[i]只出现过一次的是第三类,多次的是第二类。


一定要用O(nlogn)的动规(新技能get)讲解

题目:
给出一个由n个数组成的序列x[1..n],找出它的最长单调上升子序列。即求最大的m和a1,
a2……,am,使得a1<a2<……<am且x[a1]<x[a2]<……<x[am]。要求时间复杂度O(nlgn)


这也是一道动态规划的经典应用。一种动态规划的状态表示描述为:m[i],1≤i≤n,表示以x[i]结尾的最长上升子序列的长度,则问题的解为 max{m[i],1≤i≤n},状态转移方程和边界条件为:
m[i]=1+max{0, m[k]|x[k]<x[i] , 1≤k<i }同时当m[i]>1时,令p[i]=k,表示最优决策,
以便在计算出最优值后构造最长单调上升子序列。
上述算法的状态总数为O(n),每个状态转移的状态数最多为O(n),每次状态转移的时间为
O(1),所以算法总的时间复杂度为O(n2)。

我们先来考虑以下两种情况:
1、若x[i]<x[j],m[i]=m[j],则m[j]这个状态不必保留。因为,可以由状态m[j]转移得到
的状态m[k] (k>j,k>i),必有x[k]>x[j]>x[i],则m[k]也能由m[i]转移得到;另一方面,
可以由状态m[i]转移得到的状态m[k] (k>j,k>i),当x[j]>x[k]>x[i]时,m[k]就无法由m
[j]转移得到。
由此可见,在所有状态值相同的状态中,只需保留最后一个元素值最小的那个状态即可。

2、若x[i]<x[j],m[i]>m[j],则m[j]这个状态不必保留。因为,可以由状态m[j]转移得到的状态m[k] (k>j,k>i),必有x[k]>x[j]>x[i],则m[k]也能由m[i]转移得到,而且m[i]>m[j],所以m[k]≥m[i]+1>m[j]+1,则m[j]的状态转移是没有意义的。
综合上述两点,我们得出了状态m[k]需要保留的必要条件:不存在i使得:x[i]<x[k]且m[i]≥m[k]。
于是,我们保留的状态中不存在相同的状态值,且随着状态值的增加,最后一个元素的值
也是单调递增的。
也就是说,设当前保留的状态集合为S,则S具有以下性质D:
对于任意i∈S, j∈S, i≠j有:m[i]≠m[j],且若m[i]<m[j],则x[i]<x[j],否则x[i]>x[j]。
下面我们来考虑状态转移:假设当前已求出m[1..i-1],当前保留的状态集合为S,下面计
算m[i]。
1、若存在状态k∈S,使得x[k]=x[i],则状态m[i]必定不需保留,不必计算。因为,不妨
设m[i]=m[j]+1,则x[j]<x[i]=x[k],j∈S,j≠k,所以m[j]<m[k],则m[i]=m[j]+1≤m[k],所以状态m[i]不需保留。
2、否则,m[i]=1+max{m[j]| x[j]<x[i], j∈S}。我们注意到满足条件的j也满足x[j]=max{x[k]|x[k]<x[i], k∈S}。同时我们把状态i加入到S中。
3、若2成立,则我们往S中增加了一个状态,为了保持S的性质,我们要对S进行维护,若存
在状态k∈S,使得m[i]=m[k],则我们有x[i]<x[k],且x[k]=min{x[j]|x[j]>x[i], j∈S}。于是状态k应从S中删去。
于是,我们得到了改进后的算法:
For i:=1 to n do
{
  找出集合S中的x值不超过x[i]的最大元素k;
  if  x[k]<x[i]  then  
  {
      m[i]:=m[k]+1;
      将状态i插入集合S;
      找出集合S中的x值大于x[i]的最小元素j;
      if  m[j]=m[i]  then  将状态j从S中删去;
  }
}
从性质D和算法描述可以发现,S实际上是以x值为关键字(也是以m值为关键字)的有序集
合。
若使用平衡树实现有序集合S,则该算法的时间复杂度为O(n*log2n)。(每个状态转移的状
态数仅为O(1),
而每次状态转移的时间变为O(log2n))


还有就是从后往前跑一个最长下降子序列的时候,要在一个单减的数列里用lower_bound,有个小小的技巧,在每个数前面加个负号就变成单增的了。

#include<cstdio>
#include<algorithm>
#include<set>
#include<cstring>

using namespace std;

#define LOCAL

#define INF 0x3f3f3f3f

int L[100100],R[100100],g[100100],N,Arr[100100],ans[100100],cnt[100100],maxl;//dp[i] is method

void Dynamic_Planning()
{
	maxl=0;
	//最长上升子序列
	memset(g,0x3f,sizeof(g));
	for(int i=1;i<=N;i++)
	{
		int k=lower_bound(g+1,g+N+1,Arr[i])-g;
		L[i]=k;
		if(g[k]==INF) maxl++;
		g[k]=Arr[i];
	}

	//最长下降子序列
	memset(g,0x3f,sizeof(g));
	for(int i=N;i>0;i--)
	{
		int k=lower_bound(g+1,g+N+1,-Arr[i])-g;
		R[i]=k;
		g[k]=-Arr[i];
	}
#ifdef LOCA
	printf("%d\nL:",maxl); for(int i=1;i<=N;i++) printf("%d ",L[i]); puts("");
	printf("R:"); for(int i=1;i<=N;i++) printf("%d ",R[i]); puts("");
#endif
}

void write()
{
	memset(ans,0,sizeof(ans));
	memset(cnt,0,sizeof(cnt));

	for(int i=1;i<=N;i++)
		if(L[i]+R[i]<maxl+1) ans[i]=1;
		else cnt[L[i]]++;

	for(int i=1;i<=N;i++)
		if(ans[i]) printf("%d",ans[i]);
		else if(cnt[L[i]]==1) printf("3");
		else printf("2");
	printf("\n");
}

int main()
{
#ifdef LOCAL
	freopen("C.in","r",stdin);
#endif

	while(scanf("%d",&N)==1)
	{
		for(int i=1;i<=N;i++) L[i]=R[i]=1,scanf("%d",&Arr[i]);
		Dynamic_Planning();
		write();
	}

#ifdef LOCAL
	while(1);
#endif

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值