给定一个序列 \(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;
}