• [bzoj3173]最长上升子序列_非旋转Treap


    最长上升子序列 bzoj-3173

        题目大意:有1-n,n个数,第i次操作是将i加入到原有序列中制定的位置,后查询当前序列中最长上升子序列长度。

        注释:1<=n<=10,000,开始序列为空。

          具体的非旋转Treap的讲解:讲解链接

          想法:显然,我们发现,我每次加入的数一定是当前序列中最大的,所以,刚刚加入的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
    ",ans);
    		root=merge(merge(t1.first,i),t1.second);
    	}
    	return 0;
    }
    

         小结:非旋转Treap相较于Treap所涉及的范围更广,但虽然代码量有所下降,但是细节更多,难度更大。

          错误:split的!k是要进行特判的,不然会死递归。

  • 相关阅读:
    React 之使用 fetch
    react-native 环境搭建
    create-react-app 配置 less
    React新的前端思维方式
    字体图标 —— IconMoon
    你不知道的javascript 之 >>
    前端的自我修养
    jquery 学习
    html的meta总结
    git基本操作 nginx基本操作
  • 原文地址:https://www.cnblogs.com/ShuraK/p/8496087.html
Copyright © 2020-2023  润新知