hdu_3564_Another LIS(线段树+LIS)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3564

题意:给你N个数的位置。数i的位置为第i个数,比如 0 0 2,表示1插在第0个位置,此时数列为{1},2插在第0个位置,此时数列为{2,1},3插在第2个位置,此时数列为{2,1,3},每插一个位置,要求输出当前最大的LIS。

题解:很巧妙的求法,首先要先用线段树插空法将原数列的位置还原出来,然后从1到n,数肯定是递增的,如果位置也是递增的,那么就肯定是最长递增数列,然后用nlogn的LIS求出答案,因为插入的顺序是按1-n的顺序插入的,我们还原位置后,直接对位置进行求LIS,即为当前数的LIS,关于LIS的求法传输门:http://blog.csdn.net/bin_gege/article/details/51789261

线段树插空法:因为最后插入的数的位置肯定是固定的,所以我们记录数后,倒着插,每插入一个数,要保证这个数的位置k前有k-1个空位,这样后面的数才能插进去

#include<cstdio>
#include<algorithm>
#define F(i,a,b) for(int i=a;i<=b;i++)
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
const int N=1e5+7;
int t,n,x,ic=1,nd[N<<2],d[N],dp[N],a[N],len;
void build(int l=1,int r=n,int rt=1){
	nd[rt]=r-l+1;//保存当前区间的空位数
	if(l==r)return;
	int m=(l+r)>>1;
	build(ls),build(rs);
}
void update(int x,int c,int l=1,int r=n,int rt=1){
	if(l==r){d[c]=l,nd[rt]=0;return;}
	int m=(l+r)>>1;nd[rt]--;
	if(x<=nd[rt<<1])update(x,c,ls);//如果左儿子的空位大于等于位置
	else update(x-nd[rt<<1],c,rs);//如果小于,就插入又儿子中,在右儿子中插入的位置应该减掉左儿子的空位数
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n),build(),len=1;
		F(i,1,n)scanf("%d",a+i),dp[i]=0;
		for(int i=n;i>0;i--)update(a[i]+1,i);
		printf("Case #%d:\n",ic++);
		F(i,1,n){
			if(i==1)dp[1]=d[1],puts("1");
			else{
				int k=lower_bound(dp+1,dp+len+1,d[i])-dp;
				if(len<k)dp[k]=d[i],len=k;
				else if(d[i]<dp[k])dp[k]=d[i];
				printf("%d\n",len);
			}
		}
		puts("");
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章