nlogn求LIS(樹狀數組)


之前一直是用二分

但是因為比較難理解,寫的時候也容易忘記怎么寫。

今天比賽講評的時候講了一種用樹狀數組求LIS的方法

(1)好理解,自然也好寫(但代碼量比二分的大)

(2)擴展性強。這個解法順帶求出以i為結尾的LIS,而很多題要用到這個數組來做

而二分的做法求得是當前長度下的最小值,不容易拓展。

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 1e3 + 10;
int a[MAXN], b[MAXN], n, m, ans; 
int dp[MAXN], f[MAXN];

inline int lowbit(int x) { return x & (-x); } 

void motify(int x, int p)
{
    for(; x <= m; x += lowbit(x))
        f[x] = max(f[x], p);
}

int get_max(int x)
{
    int res = 0;
    for(; x; x -= lowbit(x))
        res = max(res, f[x]);
    return res;
}

int main()
{
    scanf("%d", &n);
    _for(i, 1, n) scanf("%d", &a[i]), b[i] = a[i];

    sort(b + 1, b + n + 1);
    m = unique(b + 1, b + n + 1) - b - 1;
    _for(i, 1, n) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
    
    int ans = 0;
    _for(i, 1, n)
    {
        dp[i] = get_max(a[i] - 1) + 1;
        ans = max(ans, dp[i]);
        motify(a[i], dp[i]); 
    }
    printf("%d\n", ans);
    return 0;
}

 

 

具體怎么做呢

n方的算法有一步去枚舉之前所有的元素比較耗時間

可以用樹狀數組優化這一步,樹狀數組維護區間最大值

把元素的值當作下標,dp值作為值

a[i]表示當前值,dp[i]表示以i為結尾最長不下降子序列的長度

則 dp[i] = get_max(a[i]) + 1

也就是說,在小於等於當前值a[i]中,最大的dp值+1就是當前的答案

不過這里有個細節,怎么區分最長不下降還是最長上升?

如果你對原理理解透徹的話,這個問題其實很容易解決,你可以停下來自己推一下,檢驗一下自己理解了沒有

 

如果是最長不下降的話,dp[i] = get_max(a[i]) + 1

如果最長上升的話, dp[i] = get_max(a[i]-1) + 1

 

最后注意要離散化一下

以下是最長上升子序列的模板

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM