HAOI 2007 上升序列

  对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1<x2<…<xm)且(ax1<ax2<…<axm)。那么就称PS的一个上升序列。如果有多个P满足条件,那么我们想求字典序最小的那个。

任务

    给出S序列,给出若干询问。对于第i个询问,求出长度为Li的上升序列,如有多个,求出字典序最小的那个(即首先x1最小,如果不唯一,再看x2最小……),如果不存在长度为Li的上升序列,则打印Impossible.

输入

    第一行一个N,表示序列一共有N个元素

    第二行N个数,为a1,a2,…,an

    第三行一个M,表示询问次数。下面接M行每行一个数Li,表示要询问长度为Li的上升序列。

输出

    对于每个询问,如果对应的序列存在,则输出,否则打印Impossible.

样例

lis.in

6

3 4 1 2 3 6

3

6

4

5

lis.out

Impossible

1 2 3 6

Impossible

数据范围

N<=10000

M<=1000

 

                          【算法简析】

    首先很容易发现,如果Li的长度大于这个序列的LIS的长度,那么一定无解,否则一定有解。为方便讨论,下文只考虑有解情况。

暂且抛开“字典序最小”这个限制条件,即任何长度为Li的上升序列Pi都可以看作符合要求,那么题目的所有要求都可以由LIS来满足。这样我们就可以直接求这个序列的LIS,然后对于所有m次询问,用O(n)的时间输出,这样算法总的时间复杂度就是O(nlogn+nm)

那么如果要求字典序最小又如何处理呢?其实我们完全可以在相同的时间复杂度内解决问题,只是需要一些转化。

注意到在求LIS的过程中,实际上我们得到的不只是LIS,而是每个数作为子序列末尾的所有最长上升序列。把问题换一个角度,假设我们知道以每个数a[i]作为开头的最长上升序列的长度lis[i],那么就很容易利用贪心思想构造出答案了。这个贪心成立的前提就是:只要以该元素为开头的LIS的长度大于等于Li,则一定有解。该结论是很显然的。

下面简述贪心的方法:

累加下标j,从左到右扫描序列,如果lis[j]Li,则答案序列的第Li个数就是a[j],并且Li减1。重复上述过程直到Li=0。最后将答案序列倒着输出即可。

这个贪心显然是成立的。由于j是从小到大逐次增加,所以第一个符合lis[j]Li的必然就是最优解。每次找到一个符合的元素,所需要的LIS的长度就会减少1。这样重复下去得到的必然是最优解。

由于只需要对序列做一遍扫描,时间复杂度最坏O(n)。构造答案的时间为O(nm),加上预处理求LISO(nlogn)的时间,总时间复杂度为O(nlogn+nm)

 

细节:求以a[i]为开头的LIS比较麻烦,可以把序列反过来后求以a[i]为末尾的LDSLongest Decreasing Subsequence),这样处理起来较方便。

 1 #include<bits/stdc++.h>
 2 const int MAXN=100010;
 3 using namespace std;
 4 int n,m,cnt,a[MAXN],f[MAXN],best[MAXN];
 5 int BinarySearch(int x){
 6     int lowerBound=1,upperBound=cnt,ans=0;
 7     while(lowerBound<=upperBound){
 8         int mid=(lowerBound+upperBound)/2;
 9         if(best[mid]>x){
10               ans=mid;
11               lowerBound=mid+1;
12         }
13         else upperBound=mid-1;
14       }
15       return ans;
16 }
17 
18 void solve(int x){
19     int last=0;
20     for(int i=1;i<=n;i++)
21     if(f[i]>=x&&a[i]>last){
22       printf("%d",a[i]);
23       if(x!=1) printf(" ");
24       last=a[i];
25       x--;
26       if(!x) break;
27     }
28       printf("\n");
29 }
30 
31 void preDP(){
32     for(int i=n;i>=1;i--){
33         int t=BinarySearch(a[i]);
34         f[i]=t+1;
35         cnt=max(cnt,t+1);
36         if(best[t+1]<a[i])
37           best[t+1]=a[i];
38       }
39 }
40 
41 int main(){
42     scanf("%d",&n);
43     for(int i=1;i<=n;i++)
44         scanf("%d",&a[i]);
45     preDP();
46     scanf("%d",&m);
47     for(int i=1;i<=m;i++){
48         int len;
49         scanf("%d",&len);
50         if(len<=cnt)
51           solve(len);
52         else puts("Impossible");
53   }
54   return 0;
55 }

 

posted @ 2015-07-28 23:50  CXCXCXC  阅读(182)  评论(0编辑  收藏  举报