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 }