hdu_3564_Another LIS(线段树+LIS)

来源:互联网 发布:域名导向服务器 编辑:程序博客网 时间:2024/06/03 09:25

题目连接: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|1using 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;}


0 0
原创粉丝点击