标签:style blog http io ar os sp for on
题目链接: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中有详细讲解。
由于题目题意要求,我们需要求出以每个数字开头的最长上升子序列长度,但是LIS最终得出的是每个数字结尾的最长子序列长度,所以我们需要把这个序列倒过来做,倒过来DP,求出在倒着的序列中,以每个数字结尾的最长下降子序列长度,这就相当于原序列中以每个数字开头的最长上升子序列长度了。
然后再输出答案即可。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 100010 using namespace std; int n,m,cnt,a[MAXN],f[MAXN],best[MAXN]; //f[i]=以第i个数字结尾的最长下降子序列长度,best[i]=长度为i的LIS的第i个元素,cnt=LIS长度 int BinarySearch(int x) //二分寻找大于x且最接近x的元素 { int lowerBound=1,upperBound=cnt,ans=0; while(lowerBound<=upperBound) { int mid=(lowerBound+upperBound)/2; if(best[mid]>x) { ans=mid; lowerBound=mid+1; } else upperBound=mid-1; } return ans; } void solve(int x) //输出长度为x的上升子序列 { int last=0; //已经输出的上升序列的最后一个数字 for(int i=1;i<=n;i++) if(f[i]>=x&&a[i]>last) { printf("%d",a[i]); if(x!=1) printf(" "); //不是输出最后一个数字,就要输出空格,BZOJ好像不会过滤掉行末空格,所以要这样做,防WA last=a[i]; x--; if(!x) break; } printf("\n"); } void preDP() //DP预处理 { for(int i=n;i>=1;i--) { int t=BinarySearch(a[i]); f[i]=t+1; cnt=max(cnt,t+1); if(best[t+1]<a[i]) best[t+1]=a[i]; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); preDP(); scanf("%d",&m); for(int i=1;i<=m;i++) { int len; scanf("%d",&len); if(len<=cnt) solve(len); else puts("Impossible"); } return 0; }
[BZOJ 1046][HAOI 2007]上升序列(nlogn的LIS算法)
标签:style blog http io ar os sp for on
原文地址:http://blog.csdn.net/qpswwww/article/details/41699651