【链接】
bozj1046
【题目大意】
给你一个序列a和序列的长度n,找一个长度为m的序列x,要求字典序最小且满足
x1<x2<…<xm
,使
ax1<ax2<…<axm
。如果不存在序列x输出Impossible。
【解题报告】
初看此题,就觉得是最长上升子序列,但是直接搞似乎好像时间(O(n*m*log(n)),实际小一点)扛不住,所以想到优化。我们可以倒着看这个序列,所以就变成求最长下降子序列,但要求x序列的字典序最小,普通求肯定是错的。定义f[x]表示做到第x个元素所能匹配的最长下降子序列的长度,所以我们可以用最长下降子序列预处理出f数组和最长的匹配长度MAX。然后输出时,判断m是否小于等于MAX,然后O(n)枚举判断还需要输出的位数是否小于等于f[i]然后输出(因为要求字典序最小),复杂度就降到了O(n*m),实际上更小一点。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10005,INF=((1<<30)-1)*2+1;
int n,m,til,a[maxn],son[maxn],que[maxn],f[maxn];
inline int Read()
{
int res=0,f=1;
char ch=getchar(),cc=ch;
while (ch<'0'||ch>'9') cc=ch,ch=getchar();
if (cc=='-') f=-1;
while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=getchar();
return res*f;
}
int Find(int x)
{
int L=0,R=til,where=0;
while (L<=R)
{
int mid=(R-L>>1)+L;
if (que[mid]>x) where=mid,L=mid+1; else R=mid-1;
}
return where;
}
void ZCJ()
{
til=0; que[0]=INF;
for (int i=n; i; i--)
{
int t=Find(a[i]);
if (t+1>til) til=t+1;
if (que[t+1]<a[i]) que[t+1]=a[i];
f[i]=t+1;
}
}
void Write(int x)
{
int las=-INF;
for (int i=1; i<=n; i++)
if (f[i]>=x&&a[i]>las)
{
printf("%d",a[i]);
las=a[i]; x--;
if (x) putchar(32); else break;
}
}
int main()
{
freopen("1046.in","r",stdin);
freopen("1046.out","w",stdout);
n=Read();
for (int i=1; i<=n; i++) a[i]=Read();
m=Read(); ZCJ();
for (int i=1; i<=m; i++)
{
int x=Read();
if (x>til) printf("Impossible"); else Write(x); putchar(10);
}
return 0;
}