转载请注明出处:http://blog.csdn.net/jiangshibiao/article/details/21646459
【题目】
1046: [HAOI2007]上升序列
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2110 Solved: 667
[ Submit][ Status]
Description
对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax2 < … < axm)。那么就称P为S的一个上升序列。如果有多个P满足条件,那么我们想求字典序最小的那个。任务给出S序列,给出若干询问。对于第i个询问,求出长度为Li的上升序列,如有多个,求出字典序最小的那个(即首先x1最小,如果不唯一,再看x2最小……),如果不存在长度为Li的上升序列,则打印Impossible.
Input
第一行一个N,表示序列一共有N个元素第二行N个数,为a1,a2,…,an 第三行一个M,表示询问次数。下面接M行每行一个数L,表示要询问长度为L的上升序列。
Output
对于每个询问,如果对应的序列存在,则输出,否则打印Impossible.
Sample Input
3 4 1 2 3 6
3
6
4
5
Sample Output
1 2 3 6
Impossible
数据范围
N<=10000
M<=1000
【分析】N<=10000,这真是不上不下的数据范围,特别是在大视野的测评系统下(算总时限的),低空飞行应该也可以过。不过,我们还是来分析一下N(log(N))的做法。首先,它要输出字典序最小的。那么我们不妨把i倒着循环枚举(防止出现转移问题。比如4,1,2,4,询问的长度为2,很有可能输出1,2)设f[i]表示从第N位到第i位的最长不上升序列的个数,然后套用经典的算法。最后,在处理每个询问的时候,只需正着枚举一边,防止转移错误。
【代码】
#include<cstdio>
using namespace std;
int a[10001],n,cnt,i,p,len,x,b[10001],f[10001],max,m;
inline int erfen(int l,int r)
{
if (l==r) return l;
int mid=(l+r)/2;
if (a[i]>=b[mid]) return erfen(l,mid);
return erfen(mid+1,r);
}
inline void print(int len,int sta)
{
for (int i=sta+1;i<=n;i++)
if (f[i]>=len&&a[i]>a[sta])
{
if (len==1) {printf("%d\n",a[i]);return;}
else printf("%d ",a[i]);
print(len-1,i);
break;
}
}
int main()
{
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
b[1]=a[n];cnt=1;f[n]=1;
for (i=n-1;i>0;i--)
{
p=erfen(1,cnt+1);
b[p]=a[i];f[i]=p;if (p>cnt) cnt=p;
if (cnt>max) max=cnt;
}
scanf("%d",&m);
for (i=1;i<=m;i++)
{
scanf("%d",&x);
if (x>max) printf("Impossible\n");
else print(x,0);
}
return 0;
}