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 }

 

你可能感兴趣的:(2007)