洛谷 P4309 最长上升子序列(平衡树维护)

传送门:

题目描述

给定一个序列,初始为空。现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。每插入一个数字,我们都想知道此时最长上升子序列长度是多少?

输入格式

第一行一个整数N,表示我们要将1到N插入序列中。
接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk。(0<=Xk<=k-1,1<=k<=N)

输出格式

N行,第i行表示i插入Xi位置后序列的最长上升子序列的长度是多少。

输入输出样例

输入#1:

3
0 0 2

输出#1:

1
1
2

思路:
这题由于题目的独特性,最长子序列中的数都没有重复的,而且每次插入的值都会保证是最大的,所以我们插入新值时,我们遍可以获得当前位置的最长上升子序列的长度为:
分裂出(小于当前位置的子树)的最长上升子序列+1
然后我们再合并回整个平衡树,并且再合并的时候,不断维护,即可得到整体区间的最大上升子序列长度。
动态转移方程:
tr[now].w = max(dp[tr[now].pos], max(tr[tr[now].ls].w, tr[tr[now].rs].w));

参考代码:

#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long ll;
const int inf = 1 << 30;
const int maxn = 2e5 + 5;
const int N = 1e3 + 5;
const int base = 131;
struct node
{
    int ls, rs, sizx, w, key, pos;
} tr[maxn];
int cnt, rt, dp[maxn], su = 1;
int rand()
{
    su = (su * base) % 100007;
    return su;
}
int newnode(int w, int i)
{
    tr[++cnt].sizx = 1, tr[cnt].w = w;
    tr[cnt].key = rand(), tr[cnt].pos = i, dp[i] = w;
    return cnt;
}
void pushup(int now)
{
    tr[now].sizx = tr[tr[now].ls].sizx + tr[tr[now].rs].sizx + 1;
    tr[now].w = max(dp[tr[now].pos], max(tr[tr[now].ls].w, tr[tr[now].rs].w));
}
void split(int now, int big, int &x, int &y)
{
    if (!now)
        x = y = 0;
    else
    {
        if (big <= tr[tr[now].ls].sizx)
            y = now, split(tr[now].ls, big, x, tr[now].ls);
        else
            x = now, split(tr[now].rs, big - 1 - tr[tr[now].ls].sizx, tr[now].rs, y);
        pushup(now);
    }
}
int merge(int x, int y)
{
    if (!x || !y)
        return x + y;
    else
    {
        if(tr[x].key>tr[y].key)
        {
            tr[x].rs=merge(tr[x].rs,y);
            pushup(x);
            return x;
        }
        else
        {
            tr[y].ls=merge(x,tr[y].ls);
            pushup(y);
            return y;
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,big;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>big;
        int x,y,z;
        split(rt,big,x,y);
        rt=merge(merge(x,newnode(tr[x].w+1,i)),y);
        cout<<tr[rt].w<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/yangzijiangac/article/details/106155147