[DP][CDQ分治] BZOJ 4553: [Tjoi2016&Heoi2016]序列

Solution S o l u t i o n

设位置 i i 最大可变成ri,最小 li l i
fi f i i i 位置的答案。容易得到

fi=max{fj+1|j<i,rjai,ajli}
(rj,aj) ( r j , a j ) 当作二位平面的一个点, ((ai,li),(0,0)) ( ( a i , l i ) , ( 0 , 0 ) ) 看作一个矩形。
就是一个类似二维数点的问题。
直接 CDQ CDQ 分治就好了。
O(nlog2n) O ( n log 2 ⁡ n )

#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
typedef pair<int, int> Pairs;

const int N = 202020;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}

int n, m, q, x, y, clc;
int mx[N], vis[N];
int a[N], l[N], r[N];
int f[N];
struct cpp {
    int x, y, z, id, tp;
    cpp(int _x = 0, int _y = 0, int _z = 0, int i = 0, int t = 0):
        x(_x), y(_y), z(_z), id(i), tp(t) {}
    inline bool operator <(const cpp &b) const {
        return x < b.x;
    }
};
cpp b[N];

inline void update(int x, int a) {
    for (x; x <= m; x += x & -x)
        if (vis[x] != clc) {
            mx[x] = a; vis[x] = clc;
        } else mx[x] = max(mx[x], a);
}
inline int query(int x) {
    int mx = 0;
    for (; x; x -= x & -x)
        if (vis[x] == clc)
            mx = max(mx, ::mx[x]);
    return mx;
}
inline void divAndConq(int l, int r) {
    static cpp tmp[N];
    if (l == r) return;
    int mid = (l + r) >> 1;
    int s1 = l, s2 = mid + 1;
    for (int i = l; i <= r; i++)
        if (b[i].z <= mid) tmp[s1++] = b[i];
        else tmp[s2++] = b[i];
    for (int i = l; i <= r; i++) b[i] = tmp[i];
    divAndConq(l, mid);
    ++clc;
    int j = l;
    for (int i = mid + 1; i <= r; i++) {
        for (; j <= mid && b[j].x <= b[i].x; j++)
            if (b[j].tp == 0)
                update(b[j].y, f[b[j].id]);
        if (b[i].tp == 1)
            f[b[i].id] = max(f[b[i].id], query(b[i].y) + 1);
    }
    divAndConq(mid + 1, r);
    int s = l; s1 = l; s2 = mid + 1;
    while (s1 <= mid && s2 <= r)
        if (b[s1] < b[s2]) tmp[s++] = b[s1++];
        else tmp[s++] = b[s2++];
    while (s1 <= mid) tmp[s++] = b[s1++];
    while (s2 <= r) tmp[s++] = b[s2++];
    for (int i = l; i <= r; i++) b[i] = tmp[i];
}

int main(void) {
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    read(n); read(q);
    for (int i = 1; i <= n; i++) {
        read(a[i]); f[i] = 1;
        l[i] = r[i] = a[i];
    }
    while (q--) {
        read(x); read(y);
        l[x] = min(l[x], y);
        r[x] = max(r[x], y);
    }
    for (int i = 1; i <= n; i++)
        m = max(r[i], m);
    q = 0;
    for (int i = 1; i <= n; i++) {
        b[++q] = cpp(a[i], l[i], q, i, 1);
        b[++q] = cpp(r[i], a[i], q, i, 0);
    }
    sort(b + 1, b + q + 1);
    divAndConq(1, q);
    cout << *max_element(f + 1, f + n + 1);
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值