题意:对于一个给定的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.
题解:
首先定义g[i]为从第i个数开始的最长上升子序列的长度。如果当前选择的数的数量+g[i]>=l,由于字典序最小,因此i一定在所求序列中且该询问一定有解。
求g[i]可以将原序列反转后直接跑一边最长上升子序列,那么g[i]=f[n-i+1]。(证明略,自己想想就明白了——其实是我懒得写了- -)
最后注意输出格式。
#include <cstdio> #include <climits> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; const int MAXN=10000+2; int a[MAXN],b[MAXN],f[MAXN],g[MAXN],n,m,l,last_num; bool cmp(int a,int b){return a>=b;} int main(){ cin >> n; for(int i=n;i;i--) scanf("%d",a+i); memset(b,171,sizeof(b)); for(int i=1;i<=n;i++){ f[i]=upper_bound(b,b+n+1,a[i],cmp)-b; b[f[i]]=max(b[f[i]],a[i]); } for(int i=1;i<=n/2;i++) swap(a[i],a[n-i+1]); for(int i=1;i<=n;i++) g[n-i+1]=++f[i];//至于为什么要++f[i],是因为用STL求出来的最长上升子序列是不包括自己的。 cin >> m; while(m--){ scanf("%d",&l); last_num=INT_MIN;//注意初始化 for(int i=1;i<=n && l;i++){ if(g[i]>=l && last_num<a[i]){ last_num=a[i],l--;//last_num记录当前已经找到的序列的最后一个元素 cout << a[i]; if(!l) cout << endl; else cout << " ";//注意输出格式 } } if(l) cout << "Impossible" << endl; } return 0; }