bzoj 3173 [Tjoi2013]最长上升子序列 (treap模拟+lis)

时间:2024-01-06 19:52:02

[Tjoi2013]最长上升子序列

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2213  Solved: 1119
[Submit][Status][Discuss]

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

Sample Output

1
1
2

HINT

100%的数据 n<=100000

Source

题解

    这道题目因为是顺序插入,求的是最长上升子序列,所以不改变当前位置的

    最长上升序列长度。

    放一个大的数在前面不影响,在中间,不影响,在后面,当当前位置为止的话也还是不影响的。

    所以只需要模拟出最后序列即可,怎么模拟,是关键。

    我是用平衡树维护的。

    点的编号即为当前插入点。

    最后求一次LIS即可。

 #include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstdio> #define ls tr[p].l
#define rs tr[p].r
#define N 100007
#define inf 1000000007
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if (ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=(x<<)+(x<<)+ch-'';ch=getchar();}
return x*f;
} int n,m,sz,rt,bh,top,now;
char flag[];
int f[N],v[N],ans[N];
struct Node
{
int l,r,val,siz,rnd;
}tr[N]; inline int rand()
{
static int seed=;
return seed=(int)((((seed^)+19260817ll)*19890604ll)%);
} inline void update(int p)
{
tr[p].siz=tr[ls].siz+tr[rs].siz+;
}
void lturn(int &p)
{
int t=tr[p].r;tr[p].r=tr[t].l;tr[t].l=p;
tr[t].siz=tr[p].siz;update(p);p=t;
}
void rturn(int &p)
{
int t=tr[p].l;tr[p].l=tr[t].r;tr[t].r=p;
tr[t].siz=tr[p].siz;update(p);p=t;
}
void ins(int &p,int x)
{
if (p==)
{
p=++sz;
tr[p].siz=,tr[p].rnd=rand();
return;
}
tr[p].siz++;
if (tr[ls].siz<x)
{
ins(rs,x-tr[ls].siz-);
if (tr[rs].rnd<tr[p].rnd) lturn(p);
}
else
{
ins(ls,x);
if (tr[ls].rnd<tr[p].rnd) rturn(p);
}
}
void dfs(int p)
{
if (!p) return;
dfs(ls);
v[++now]=p;
dfs(rs);
}
void solve()
{
memset(f,,sizeof(f)),f[]=-inf;
for (int i=;i<=n;i++)
{
int t=upper_bound(f,f+top+,v[i])-f;
if (f[t-]<=v[i])
{
f[t]=min(f[t],v[i]);
ans[v[i]]=t;
top=max(t,top);
}
}
}
int main()
{
n=read();
for (int i=;i<=n;i++)
{
int x=read();now=i;
ins(rt,x);
}
now=,dfs(rt);
solve();
for (int i=;i<=n;i++)
{
ans[i]=max(ans[i-],ans[i]);
printf("%d\n",ans[i]);
}
}