BZOJ1046(HAOI2007)[上升序列]--最长下降子序列预处理

【链接】
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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值