对于一个给定的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.
转载请注明出处:http://blog.csdn.net/jiangshibiao/article/details/21646459
【题目】
对于一个给定的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.
第一行一个N,表示序列一共有N个元素第二行N个数,为a1,a2,…,an 第三行一个M,表示询问次数。下面接M行每行一个数L,表示要询问长度为L的上升序列。
对于每个询问,如果对应的序列存在,则输出,否则打印Impossible.
【分析】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; }