标签: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