• BZOJ3173(平衡树+最长上升子序列)


    传送门

    哇!这道题花了我一下午加一晚上,主要还是因为没有理解的很透彻就开始敲代码的缘故吧!现在要清清楚楚地分析一遍。

    题意的重点还是要说一下,第i次插入i,但插入的位置不一定是按顺序的,题给的就是每个数插入的位置,求的是每插入一个数就回答当前最长上升子序列长度


    比如样例:

    3
    0 0 2

    就是一个

    1

    2 1

    2 1 3

    的过程。

    注意:真正插入的位置是输入的x再加1


    分析得知,每次插入一个新数x,都一定是当前序列中的最大值。那么对于当前序列的最长上升子序列,要么是以x结尾的最长上升子序列,要么是在前面就有一个更长的序列(以其他数为结尾的最长上升子序列)

    而且,输出的答案一定是单调不降的

    那我们有这样的一个做法:

    对于每一个数x,都把它插入平衡树中,同时维护平衡树中目前序列的最长上升子序列的长度maxn(可能并不是以x结尾)。

    根据平衡树性质:x旋到root后,ch[x][0]即为当前序列x前面的数,答案也自然是从前面的序列更新而来。

    重要的是update的时候要维护maxn

    void update(int x)
    {
        siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
        maxn[x]=g[x];
        maxn[x]=max(maxn[x],maxn[ch[x][0]]);
        maxn[x]=max(maxn[x],maxn[ch[x][1]]);
    }
    update

    还有一点要注意的是:

    平衡树里插入的是位置

    插入时,假如要求插入到第k个位置,应该要先找到第k大再插入,而不是直接插入,因为输入的x可能是一样的,但序列早就变了。

    找到第x大(因为要插入的位置是x+1),把它旋转到root,然后把第x+1大(后面称为m)旋到root的右儿子,再把要插入的数旋到m的左儿子。

    #include<bits/stdc++.h>
    #define INF 2100000000
    #define N 100003 
    using namespace std;
    int read()
    {
        int f=1,x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    int root=-INF;
    int ans=-INF,n;
    int num=0,tot=0;
    int f[N],ch[N][4],siz[N],key[N],maxn[N],g[N];
    int get(int x)
    {
        return ch[f[x]][1]==x;
    }
    void update(int x)
    {
        siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
        maxn[x]=g[x];
        maxn[x]=max(maxn[x],maxn[ch[x][0]]);
        maxn[x]=max(maxn[x],maxn[ch[x][1]]);
    }
    void rotate(int x)
    {
        int old=f[x],oldf=f[old];
        int which=get(x);
        ch[old][which]=ch[x][which^1];f[ch[old][which]]=old;
        f[old]=x;ch[x][which^1]=old;
        f[x]=oldf;
        if(oldf) ch[oldf][ch[oldf][1]==old]=x;
        update(old);update(x);
    }
    void splay(int x,int tar)
    {
        for (int fa;(fa=f[x])!=tar;rotate(x))  
         if (f[fa]!=tar)  
          rotate(get(x)==get(fa)?fa:x);  
        if (!tar)  
         root=x;  
    }
    int find(int x){
        int now=root;
        while (1){
            if (ch[now][0]&&x<=siz[ch[now][0]])
                now=ch[now][0];
            else{
                    int temp=siz[ch[now][0]]+1;
                    if (x==temp)
                       return now;
                    x-=temp;now=ch[now][1];
                }
        }
    }    
    int main()
    {
        n=read();
        root=++num; key[num]=1000000000; siz[root]=1; f[root]=0; ch[root][1]=++num;
        key[num]=0; siz[num]=1; ch[num][1]=ch[num][0]=0; f[num]=root;
        for(int i=1;i<=n;++i)
        {
            int x=read();x++;
            int m=find(x);
            int n=find(x+1);
            splay(m,0);
            splay(n,m);
            int t=ch[root][1]; 
            ch[t][0]=++num;f[num]=t;key[num]=i;ch[num][1]=ch[num][0]=0;siz[num]=1;
            splay(t,0); splay(num,0); 
            g[num]=maxn[ch[root][0]]+1;
            update(num);
            printf("%d
    ",maxn[num]);
        }
    }
    BZOJ3173

    还有就是常常要splay一下保证复杂度


    还有一种做法是先处理完最终的序列,再用树状数组维护最长上升子序列(看日后什么时候去码一下)。

        

  • 相关阅读:
    小程序中template的用法
    小程序弹窗的几种形式
    js怎样截取以'-'分割的字符串
    js怎样截取字符串后几位以及截取字符串前几位
    局域网聊天软件项目小结(1)
    IPAddress类
    Combobox 成员添加
    tcpclient 类
    console.read()读入的内容
    技术带来的进步与退步---一点点反思
  • 原文地址:https://www.cnblogs.com/yyys-/p/11173060.html
Copyright © 2020-2023  润新知