1. 程式人生 > >[bzoj3173]最長上升子序列_非旋轉Treap

[bzoj3173]最長上升子序列_非旋轉Treap

向上 ont clas ring pos pan rand() names include

最長上升子序列 bzoj-3173

    題目大意:有1-n,n個數,第i次操作是將i加入到原有序列中制定的位置,後查詢當前序列中最長上升子序列長度。

    註釋:1<=n<=10,000,開始序列為空。

      想法:顯然,我們發現,我每次加入的數一定是當前序列中最大的,所以,剛剛加入的i,要麽是當前序列中LIS的結尾,要麽不屬於LIS。根據這個性質,我們想到:在Treap中維護這樣的性質,就是維護每個數加入節點的編號。然後,我們更新新節點的方式就是它的左子樹和右子樹的LIS取最大+1。其實最重要的就是如何加入這個新的節點?我們引進非旋轉Treap。

        非旋轉Treap在結構上和Treap一樣,都是維護平衡的BST。但是,非旋轉Treap在實現上更加暴力和簡單。有兩個操作,撕裂和合並。

        撕裂?就是去除BST上的一條邊,使得原來的非旋轉Treap變成兩顆樹。這樣我們就可以對於撕裂開的新子樹進行一些單獨的處理。例如我們可以撕裂兩次,得到一段區間,那麽在進行區間反轉時就可以僅僅維護一段子樹就可以了。

        合並?就是將撕裂之後的子樹進行合並即可。這樣的話我們對於BST節點的一個權值是隨機的,所以我們在合並時還要維護Treap的基本性質。

      這道題的實現和理解無疑是非旋轉Treap的裸題,但是非旋轉Treap強大的地方已經得到體現。

    最後,附上醜陋的代碼... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 300100
#define mp make_pair
using namespace std;
typedef pair<int,int> par;
int n;
int maxx[N],lis[N],size[N],ls[N],rs[N],key[N];
//maxx表示當前節點子樹的LIS的長度
//lis表示當前子樹中以當前節點結尾的不下降子序列長度
int root;
void update(int x)//這是向上更新
{
	maxx[x]=lis[x];
	size[x]=size[ls[x]]+size[rs[x]]+1;
	maxx[x]=max(maxx[x],max(maxx[ls[x]],maxx[rs[x]]));
}
int lson,rson;
par split(int x,int k)//撕裂操作,返回的是撕裂後兩棵新樹的根節點,用pair來存儲
{
	if(!k) return mp(0,x);
	lson=ls[x],rson=rs[x];par t;
	if(k==size[ls[x]])
	{
		ls[x]=0;update(x);
		return mp(lson,x);
	}
	if(k==size[ls[x]]+1)
	{
		rs[x]=0;update(x);
		return mp(x,rson);
	}
	if(k<size[ls[x]])
	{
		t=split(lson,k);
		ls[x]=t.second;update(x);
		return mp(t.first,x);
	}
	t=split(rson,k-size[ls[x]]-1);
	rs[x]=t.first;update(x);
	return mp(x,t.second);
}
int merge(int x,int y)//合並操作,返回的是合並之後的新樹的根節點
{
	if(!x|!y)
	{
		return x|y;
	}
	if(key[x]<key[y])
	{
		ls[y]=merge(x,ls[y]);update(y);
		return y;
	}
	rs[x]=merge(rs[x],y);update(x);
	return x;
}
int main()
{
	scanf("%d",&n);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		size[i]=1;
		key[i]=rand()*rand();
		par t1;
		t1=split(root,x);
		lis[i]=maxx[i]=maxx[t1.first]+1;
		ans=max(ans,lis[i]);
		printf("%d\n",ans);
		root=merge(merge(t1.first,i),t1.second);
	}
	return 0;
}

    小結:非旋轉Treap相較於Treap所涉及的範圍更廣,但雖然代碼量有所下降,但是細節更多,難度更大。

      錯誤:split的!k是要進行特判的,不然會死遞歸。

[bzoj3173]最長上升子序列_非旋轉Treap