洛谷 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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章