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;
}


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