BZOJ3173 [Tjoi2013]最长上升子序列(离线处理+Treap+LIS)

【题解】

离线处理:


第n个数的插入不会改变前n-1个数的相对位置,因此可以直接求得最终序列,第i次操作的答案就是仅含1~i的LIS 
最终序列可以用Treap求得;

由于仅含1~i的LIS = max( 仅含1~i-1的LIS , 以i为结尾的LIS ),可以用O(N*logN)的动态规划求出以每个数为结尾的LIS,再递推求出每步答案 


【代码】

#include<stdio.h>
#include<stdlib.h>
int a[100005],ans[100005],g[100005];
int n,p=0,p2=0;
int max(int a,int b)
{
	if(a>b) return a;
	return b;
}
struct Node
{
	Node* ch[2];
	int v,r,s;
	int cmp_s(int x) const
	{
		if( x <= ch[0]->s + 1 ) return 0;
		return 1;
	}
};
Node *root,*null;
void init()
{
	null=new Node();
	null->ch[0] = null->ch[1] = NULL;
	null->v=null->r=null->s=0;
	root=null;
}
void gets(Node* &o)
{
	o->s = o->ch[0]->s + 1 + o->ch[1]->s;
}
void xz(Node* &o,int d)
{
	Node* k=o->ch[d^1];
	o->ch[d^1]=k->ch[d];
	k->ch[d]=o;
	gets(o);
	gets(k);
	o=k;
}
void tj(Node* &o,int x)
{
	if(o==null)
	{
		o=new Node();
		o->ch[0] = o->ch[1] = null;
		o->v=++p;
		o->r=rand();
		o->s=1;
		return;
	}
	int d=o->cmp_s(x);
	o->s++;
	if(d==1) x-= o->ch[0]->s + 1;
	tj(o->ch[d],x);
	if( o->ch[d]->r < o->r ) xz(o,d^1);
}
void geta(Node* &o)
{
	if(o->ch[0]!=null) geta(o->ch[0]);
	a[++p2]=o->v;
	if(o->ch[1]!=null) geta(o->ch[1]);
}
int find(int num,int left,int right)
{
	int mid;
	while(left<right)
	{
		mid=(left+right+1)/2;
		if(g[mid]>num) right=mid-1;
		else left=mid;
	}
	return left;
}
int main()
{
	srand(25);
	int i,x,len=0;
	init();
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&x);
		tj(root,x+1);
	}
	geta(root);
	for(i=1;i<=n;i++)
	{
		x=find(a[i],0,len)+1;
		ans[a[i]]=x;
		g[x]=a[i];
		len=max(len,x);
	}
	for(i=1;i<=n;i++)
	{
		ans[i]=max(ans[i-1],ans[i]);
		printf("%d\n",ans[i]);
	}
	return 0;
}


你可能感兴趣的:(dp,LIS,平衡树,离线处理)