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个空位,这样后面的数才能插进去

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define F(i,a,b) for(int i=a;i<=b;i++)
 4 #define ls l,m,rt<<1
 5 #define rs m+1,r,rt<<1|1
 6 using namespace std;
 7 const int N=1e5+7;
 8 int t,n,x,ic=1,nd[N<<2],d[N],dp[N],a[N],len;
 9 void build(int l=1,int r=n,int rt=1){
10     nd[rt]=r-l+1;//保存当前区间的空位数
11     if(l==r)return;
12     int m=(l+r)>>1;
13     build(ls),build(rs);
14 }
15 void update(int x,int c,int l=1,int r=n,int rt=1){
16     if(l==r){d[c]=l,nd[rt]=0;return;}
17     int m=(l+r)>>1;nd[rt]--;
18     if(x<=nd[rt<<1])update(x,c,ls);//如果左儿子的空位大于等于位置
19     else update(x-nd[rt<<1],c,rs);//如果小于,就插入又儿子中,在右儿子中插入的位置应该减掉左儿子的空位数
20 }
21 int main(){
22     scanf("%d",&t);
23     while(t--){
24         scanf("%d",&n),build(),len=1;
25         F(i,1,n)scanf("%d",a+i),dp[i]=0;
26         for(int i=n;i>0;i--)update(a[i]+1,i);
27         printf("Case #%d:\n",ic++);
28         F(i,1,n){
29             if(i==1)dp[1]=d[1],puts("1");
30             else{
31                 int k=lower_bound(dp+1,dp+len+1,d[i])-dp;
32                 if(len<k)dp[k]=d[i],len=k;
33                 else if(d[i]<dp[k])dp[k]=d[i];
34                 printf("%d\n",len);
35             }
36         }
37         puts("");
38     }
39     return 0;
40 }
View Code

 


 

posted @ 2016-07-01 22:28  bin_gege  阅读(340)  评论(0编辑  收藏  举报