bzoj3173 最长上升子序列 题解--Treap+nlogn求LIS
Description
给定一个序列,初始为空。现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。每插入一个数字,我们都想知道此时最长上升子序列长度是多少?
Input
第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk(0<=Xk<=k-1,1<=k<=N)
Output
N行,第i行表示i插入Xi位置后序列的最长上升子序列的长度是多少。
Sample Input
3
0 0 2
0 0 2
Sample Output
1
1
2
1
2
HINT
100%的数据 n<=100000
题解
树的部分比较好理解,读到了位置insert即可,注意插入的时候关键字是cnt,也就是当前数的值。
然后LIS的部分今天才学会,就是nlogn的办法,如果不用stl的话就是二分查找,网上详解很多,在此不多说。
代码
#include<algorithm> #include<cstring> #include<cstdio> using namespace std; int v,n,dp[210000],b[210000],d[210000],cnt,tot,ans[210000],mx,root,p; struct treap{ int ls,rs,pri,siz,val; }a[200010]; void pushup(int o){ a[o].siz=a[a[o].ls].siz+a[a[o].rs].siz+1; return; } void lturn(int &o){ int t=a[o].rs; a[o].rs=a[t].ls; a[t].ls=o; a[t].siz=a[o].siz; pushup(o); o=t; } void rturn(int &o){ int t=a[o].ls; a[o].ls=a[t].rs; a[t].rs=o; a[t].siz=a[o].siz; pushup(o); o=t; } void insert(int p,int &o){ if(!o){ o=++cnt; a[o]=(treap){0,0,rand(),1,1}; return; } a[o].siz++; // if(p<=a[a[o].ls].siz){ insert(p,a[o].ls); if(a[o].pri<a[a[o].ls].pri)rturn(o); } else { insert(p-a[a[o].ls].siz-1,a[o].rs); if(a[o].pri<a[a[o].rs].pri)lturn(o); } } void dfs(int o){ if(a[o].ls!=0)dfs(a[o].ls); b[++tot]=o; if(a[o].rs!=0)dfs(a[o].rs); } void getlis(){ memset(d,0x3f,sizeof(d)); d[0]=-1000000; for(int i=1;i<=tot;i++){ int t=upper_bound(d,d+mx+1,b[i])-d; if(d[t-1]<=b[i])d[t]=min(d[t],b[i]); ans[b[i]]=t; mx=max(mx,t); } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&p); insert(p,root); } dfs(root); getlis(); for(int i=1;i<=n;++i){ ans[i]=max(ans[i-1],ans[i]); printf("%d\n",ans[i]); } return 0; }