1. 程式人生 > 其它 >[HEOI2016/TJOI2016]序列(cdq優化dp)

[HEOI2016/TJOI2016]序列(cdq優化dp)

真實情景下應用 cdq 分治優化 dp 初體驗。

給定一個序列 \(a\) 和若干對整數 \(x,y\),表示在一次變化中, \(a_x \gets y\)。這次變化結束後,\(a\)復原。試選出一個子序列,使其在任意一次變化中都滿足單調不降。

LIS 問題作為 dp 入門題十分熟悉,即 \(f_i=\max\{f_j\}+1\),要求 \(j\) 滿足 \(j<i,a_j\le a_i\)。這道題實際上是增加了兩條限制: \(max_j\le a_i,a_j\le min_i\)。這裡的 \(max_x,min_x\) 表示 \(a_x\) 可能變化為的最大、最小值。

觀察這四條限制,不難發現:

  • \(a_j\le a_i\) 被包含在新加的兩條限制中,不用單獨考慮;
  • \(j<i\) 可以直接轉化為 cdq 分治的時間維!

標準的三維偏序問題,cdq 分治+樹狀陣列處理左邊對右邊的貢獻即可。時間複雜度 \(O(n\log n)\)

注意:要計算不發生變化時的情況;cdq 時先遞迴左邊,然後計算左對右的貢獻,再遞迴右邊。

下面是 AC 程式碼:

#include <iostream>
#include <cstdio>
#include <algorithm>
using std::max;
using std::min;
using std::sort;
inline int rd()
{
    int x = 0, f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar())
        f ^= (c == '-');
    for (; isdigit(c); c = getchar())
        x = (x << 1) + (x << 3) + (c ^ 48);
    return f ? x : -x;
}
const int N = 1e5 + 5;
const int V = 1e5;
int n, a[N], mx[N], mn[N], p[N], f[N], c[N];
#define lowbit(x) (x & (-x))
inline void ins(int x, int y)
{
    for (; x <= V; x += lowbit(x))
        c[x] = max(c[x], y);
}
inline int ask(int x)
{
    int res = 0;
    for (; x; x -= lowbit(x))
        res = max(res, c[x]);
    return res;
}
inline void clr(int x)
{
    for (; x <= V; x += lowbit(x))
        c[x] = 0;
}
void cdq(int l, int r)
{
    if (l == r)
        return f[l] = max(f[l], 1), void();
    int mid = (l + r) >> 1;
    cdq(l, mid);
    for (int i = l; i <= r; ++i)
        p[i] = i;
    sort(p + l, p + mid + 1, [](int x, int y)
         { return mx[x] < mx[y]; });
    sort(p + mid + 1, p + r + 1, [](int x, int y)
         { return a[x] < a[y]; });
    for (int i = mid + 1, j = l; i <= r; ++i)
    {
        while (j <= mid && mx[p[j]] <= a[p[i]])
            ins(a[p[j]], f[p[j]]), ++j;
        f[p[i]] = max(f[p[i]], ask(mn[p[i]]) + 1);
    }
    for (int i = l; i <= mid; ++i)
        clr(a[i]);
    cdq(mid + 1, r);
}
int main()
{
    n = rd();
    int m = rd(), ans = -1;
    for (int i = 1; i <= n; ++i)
        a[i] = mx[i] = mn[i] = rd();
    for (int i = 1; i <= m; ++i)
    {
        int x = rd(), y = rd();
        mx[x] = max(mx[x], y);
        mn[x] = min(mn[x], y);
    }
    cdq(1, n);
    for (int i = 1; i <= n; ++i)
        ans = max(ans, f[i]);
    printf("%d\n", ans);
    return 0;
}

THE END