HAOI 2007 上升序列

  对于一个给定的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.

输入

第一行一个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),加上预处理求LIS的O(nlogn)的时间,总时间复杂度为O(nlogn+nm)。

细节:求以a[i]为开头的LIS比较麻烦,可以把序列反过来后求以a[i]为末尾的LDS(Longest 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 }
时间: 2024-10-24 08:35:04

HAOI 2007 上升序列的相关文章

BZOJ 1046 HAOI 2007 上升序列 DP

题目大意:给出一个序列,求出字典序最小的长度为k的上升序列. 思路:先随便搞搞求出一个数组f,表示从i开始最长的上升序列的长度.然后贪心的往后找,能放在当前位置就放. CODE: #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 10010 #define INF 0

[BZOJ 1046][HAOI 2007]上升序列(nlogn的LIS算法)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1046 有人说这题是NOIP难度?表示怀疑,蒟蒻认为此题难度略大于NOIP.... 这个题的序列长度n<=1e4,如果用n^2的LIS做法肯定TLE,只能用nlogn的算法,这种算法在http://www.slyar.com/blog/longest-ordered-subsequence.html中有详细讲解. 由于题目题意要求,我们需要求出以每个数字开头的最长上升子序列长度,但

[BZOJ 1053] [HAOI 2007]反素数ant

1053: [HAOI2007]反素数ant Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1857  Solved: 1034[Submit][Status][Discuss] Description 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6)=4.如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数.例如,整数1,2,4,6等都是反质数.现在给定一个数N,你能求出不超过N的最大的反质数么?

【HAOI 2007】【BZOJ 1053】反素数ant

虽然这题很水,但蒟蒻还是想了很久. 首先由一个很显然的结论,这题只会用到10个质数. 一个感性的证明: 设有一数x=pk11+pk22+...+pkmm 那么我们现在要给x乘上一个质数,使它的约数数目最多且相对较小. 显然我们我们要乘第1≤i≤m+1个质数,如果乘再靠后的质数,显然不如乘第m+1个更优. 于是我们看到2?3?5?7?11?13?17?19?23?29=6469693230>2?109 所以只需要10个质数. 然后想了很久继续用数学方法做,无果,后来发现其实是搜索= = code:

[BZOJ 1047][HAOI 2007]理想的正方形(二维滑动窗口+单调队列)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1047 思路:裸的二维上的滑动窗口问题,可以借鉴一维滑动窗口的思路.首先预处理出每一列j的.以第i行元素为结尾.长度为n的区间的最大值maxv[i][j].最小值minv[i][j],然后再搞每一行,求出以每一行i结尾.行标上长度为n的区间.以第j列结尾.列标上长度为n的区间得到的二维区间最大值与最小值之差,遍历每一行得到这个差的最小值即为答案. #include <iostrea

[HAOI 2007]反素数ant

Description 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6)=4. 如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数.例如,整数1,2,4,6等都是反质数. 现在给定一个数N,你能求出不超过N的最大的反质数么? Input 一个数N(1<=N<=2,000,000,000). Output 不超过N的最大的反质数. Sample Input 1000 Sample Output 840 题解 拿到题首先准确无误地题干看错,

BZOJ 1047 HAOI 2007 理想的正方形 单调队列

题目大意:给出一个矩阵,求出一个k*k的子矩阵,使得这个矩阵中最大值和最小值的差最小,输出这个差值. 思路:利用单调队列维护每一行的数字,求出一个数字前面k个数字中的最大值和最小值,然后在列上暴力求出真个矩阵的最大值和最小值,总时间复杂度O(M*M+M*M*K). CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algor

[HNOI2001]求正整数

题目描述 对于任意输入的正整数n,请编程求出具有n个不同因子的最小正整数m. 例如:n=4,则m=6,因为6有4个不同整数因子1,2,3,6:而且是最小的有4个因子的整数. 输入输出格式 输入格式: n(1≤n≤50000) 输出格式: m 输入输出样例 输入样例#1: INT.IN 4 输出样例#1: INT.OUT 6题解: 这道题和[HAOI 2007]反素数ant解题思路和方法简直一毛一样... 同样我们引入这个公式: 对任一整数a>1,有a=p1a1p2a2-pnan,其中p1<p2

一本通1604理想的正方形

1604:理想的正方形 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:HAOI 2007 有一个 a×b 的整数组成的矩阵,现请你从中找出一个 n×n 的正方形区域,使得该区域所有数中的最大值和最小值的差最小. [输入] 第一行为三个整数,分别表示 a,b,n 的值: 第二行至第 a+1 行每行为 b 个非负整数,表示矩阵中相应位置上的数. [输出] 输出仅一个整数,为 a×b 矩阵中所有「n×n 正方形区域中的最大整数和最小整数的差值」的最