• [BZOJ]1046 上升序列(HAOI2007)


      和字典序有关的题型啊。

    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

      6
      3 4 1 2 3 6
      3
      6
      4
      5

    Sample Output

      Impossible
      1 2 3 6
      Impossible

    HINT

      N<=10000,M<=1000。

    Solution

      看到字典序和上升子序列DP,小C想起了APIO2009的那道convention。

      (不要吐槽小C的做题顺序)

      既然要取字典序最小的,那肯定就是从小的取起,能取就取。

      那什么情况算是能取的呢?

      设要取的上升序列长度为len,已经取了x个数,且最后一个数大小为last。

      设我们当前要取的数的位置为i,数的大小为a[i],以这个位置开头的最长上升子序列长度为d[i]。

      那么如果a[i]>last且d[i]+x>=len那么i这个位置的数就能取,也就是必须取。

      我们来分析为什么这两个条件可以决定这个数取不取。

        a[i]>last是上升序列的必要条件,我们已经满足了last必须取,且上一个取last一定能构造出答案,所以从字典序最小的角度来说,我们不能用a[i]来替换last。

        d[i]+x>=len则是能构造出长度为len的答案的充分条件,因为d[i]满足二分性,如果d[i]>=len-x,那么一定存在一个以a[i]开头长度为len-x的上升序列接在last后面,从字典序最小的角度来说,我们取它显然最优。

      剩下就是求d[i]了嘛。求最长上升子序列谁不会啊?

      其实是正常的字典序也好,还是这题的逗比字典序也好,用以上的思路都是可以做的。

      时间复杂度O(nlogn+n*m)。

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #define MN 10005
    #define MM 1005
    using namespace std;
    struct meg{int val,pos;}b[MM];
    int f[MN],a[MN],p[MN],lb[MM],ans[MM][MN],t[MN<<2];
    int nin,n,m,MQ;
    
    inline int read()
    {
        int n=0,f=1; char c=getchar();
        while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
        while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}
        return n*f;
    }
    
    void dfs(int depth,int x,int L,int R,int ps)
    {
        register int i,j,dx,mx=L-1;
        for (i=x;i<=n;++i)
        {
            if (a[i]<=ps) continue;
            dx=lower_bound(lb+1,lb+m+1,f[i]+depth)-lb-1;
            if (dx>mx)
            {
                for (j=mx+1;j<=dx;++j) ans[b[j].pos][depth]=a[i],mx=lb[j]==depth?j:mx;
                if (dx>mx) {dfs(depth+1,i+1,mx+1,dx,a[i]); mx=dx;}
                if (mx==R) break;
            }
        }
    }
    
    inline void getad(int x,int z) {for (x+=MQ;x&&z>t[x];x>>=1) t[x]=z;}
    inline int getmx(int L,int R)
    {
        if (L>R) return 0;
        register int lt=0;
        for (L+=MQ,R+=MQ;L<=R;L>>=1,R>>=1)
        {
            if ( L&1) lt=max(lt,t[L++]);
            if (~R&1) lt=max(lt,t[R--]);
        }
        return lt;
    }
    bool cmp(const meg& a,const meg& b) {return a.val<b.val;}
    
    int main()
    {
        register int i,j;
        n=read();
        for (i=1;i<=n;++i) a[i]=p[i]=read();
        sort(p+1,p+n+1);
        nin=unique(p+1,p+n+1)-p-1;
        for (i=1;i<=n;++i) a[i]=lower_bound(p+1,p+nin+1,a[i])-p;
        for (MQ=1;MQ<nin;MQ<<=1); --MQ;
        for (i=n;i;--i) f[i]=getmx(a[i]+1,nin)+1,getad(a[i],f[i]);
        m=read();
        for (i=1;i<=m;++i) b[i].val=read(),b[i].pos=i;
        sort(b+1,b+m+1,cmp);
        for (i=1;i<=m;++i) lb[i]=b[i].val;
        dfs(1,1,1,m,0);
        for (i=1;i<=m;++i)
        {
            if (!ans[i][1]) {puts("Impossible"); continue;}
            printf("%d",p[ans[i][1]]);
            for (j=2;ans[i][j];++j) printf(" %d",p[ans[i][j]]); puts("");
        }
    }

    Last Word

      不知道为什么突然想把询问排序然后一起做想降低常数,不过常数好像更大了233。

      因为看错字典序的缘故,小C的代码画风有点崩坏……(其实本来就很崩)

  • 相关阅读:
    文本框模糊匹配(纯html+jquery简单实现)
    ajax 基础2
    ajax 基础
    Js 实战3(实现全选)
    JS 实战2(邮箱选人功能)
    JS 实战1(添加、删除)
    Js 图片轮播渐隐效果
    Js 手风琴效果
    树上莫队
    洛谷 3676
  • 原文地址:https://www.cnblogs.com/ACMLCZH/p/7452546.html
Copyright © 2020-2023  润新知