[HEOI2016/TJOI2016]序列(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

上一篇:确认输入密码的基


下一篇:2021.10.06 膜你赛