luoguP4093、BZOJ4553[HEOI2016&TJOI2016]序列

题目链接

洛谷

BZOJ

建议交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;
}

猜你喜欢

转载自www.cnblogs.com/Rhein-E/p/10419435.html