题目链接
建议交BZOJ,洛谷数据比较弱
解析
一看到这种长得跟最长上升子序列很像的东西就想到dp
不难写出dp方程:
[dp[i] = max_{j < i, val[j] le min[i],max[j] le val[i]} {dp[j] + 1}
]
其中(max[i]),(min[i]),(val[i])分别表示序列第(i)位的最大取值、最小取值和初始值
然后观察限制条件,发现是个三位偏序问题,就可以套CDQ了。。。。
代码(含注释)
PS.算是第一道自己想出来的CDQ吧,我好菜啊QAQ
再PS.CDQ都不会写了,最开始树状数组清零直接memset,结果T飞QAQ
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 100005
typedef long long LL;
struct Node {
int val, mx, mn, id;
bool operator <(const Node &t) const { return mn < t.mn; }
} a[MAXN], b[MAXN];
int dp[MAXN], tree[MAXN];
int N, M, ans;
void solve(int, int);
void update(int, int);
int query(int);
int main() {
std::ios::sync_with_stdio(false);
std::cin >> N >> M;
for (int i = 1; i <= N; ++i) {
std::cin >> a[i].val;
a[i].mx = a[i].mn = a[i].val;
a[i].id = i;
}
while (M--) {
int x, y;
std::cin >> x >> y;
a[x].mx = std::max(a[x].mx, y);
a[x].mn = std::min(a[x].mn, y);
}
//保证分治的时候右半区间mn有序
std::sort(a + 1, a + 1 + N);
solve(1, N);
for (int i = 1; i <= N; ++i)
ans = std::max(ans, dp[i]);
std::cout << ans << std::endl;
return 0;
}
void solve(int l, int r) {
if (l == r) {
dp[a[l].id] = std::max(dp[a[l].id], 1);
return;
}
int mid = (l + r) >> 1, p1, p2;
//保证左半区间位置在右半区间之前,对应条件一
p1 = l, p2 = mid + 1;
for (int i = l; i <= r; ++i)
if (a[i].id <= mid) b[p1++] = a[i];
else b[p2++] = a[i];
for (int i = l; i <= r; ++i)
a[i] = b[i];
//先算出左半区间的dp值,并按val排序
solve(l, mid);
//更新右半区间的dp值
p1 = l, p2 = mid + 1;
while (p2 <= r) {
while (p1 <= mid && a[p1].val <= a[p2].mn)//保证条件二成立
update(a[p1].mx, dp[a[p1].id]), ++p1;
dp[a[p2].id] = std::max(dp[a[p2].id], query(a[p2].val) + 1);//这一行和上面一行保证条件三成立
++p2;
}
for (int i = l; i <= mid; ++i)
update(a[i].mx, 0);//还原树状数组,直接memset会T飞。。。
//继续计算右半区间的dp值,并按val排序
solve(mid + 1, r);
//将[l,r]按val排序,过程类似归并排序
p1 = l, p2 = mid + 1;
for (int i = l; i <= r; ++i)
if (p1 > mid) b[i] = a[p2++];
else if (p2 > r) b[i] = a[p1++];
else b[i] = (a[p1].val < a[p2].val ? a[p1++] : a[p2++]);
for (int i = l; i <= r; ++i)
a[i] = b[i];
}
inline void update(int pos, int v) {
for (int i = pos; i < MAXN; i += (i & -i))
if (v) tree[i] = std::max(tree[i], v);
else tree[i] = 0;
}
inline int query(int pos) {
int res = 0;
for (int i = pos; i; i -= (i & -i))
res = std::max(tree[i], res);
return res;
}