「Fortuna OJ」4606 – 序列 / HEOI 2016 Sequence

主要思路

由于本人分治很菜,所以就来刷分治题了。

先思考 50 分\(O(n^2)\)的做法。记录每一个位置的原始值、变化后的最大值、变化后的最小值,然后发现如果一个位置\(i\)对后面的位置\(j\)有贡献当且仅当:

\[a_i < minVal_j \ (1) \\ maxVal_i < a_j \ (2) \]

所以这就相当于一个二位偏序的题目,有两个限制。我们可以用整体二分,在做完左区间之后,左区间按原来的值进行排序,而右区间按最小值进行排序,即可满足第一个条件;第二个条件可以用线段树来搞,搞两个指针来比较大小,然后用线段树来统计答案即可。非常好写。

代码

// FOJ4606.cpp
#include <bits/stdc++.h>
#define lson (p << 1)
#define rson ((p << 1) | 1)
#define mid ((l + r) >> 1)

using namespace std;

const int MAX_N = 1e5 + 200;

int tree[MAX_N << 2], n, m, low[MAX_N], up[MAX_N], dp[MAX_N], seq[MAX_N], idx[MAX_N];
int max_range;
bool tag[MAX_N << 2];

bool compareBySequenceVal(int a, int b) { return seq[a] < seq[b]; }
bool compareByMinimalVal(int a, int b) { return low[a] < low[b]; }

inline void pushup(int p) { tree[p] = max(tree[lson], tree[rson]); }

inline void pushdown(int p)
{
    if (tag[p])
    {
        tree[lson] = tree[rson] = 0;
        tag[lson] = tag[rson] = tag[p];
        tag[p] = false;
    }
}

void update(int qx, int l, int r, int p, int val)
{
    if (l == r)
    {
        tree[p] = max(tree[p], val);
        return;
    }
    pushdown(p);
    if (qx <= mid)
        update(qx, l, mid, lson, val);
    else
        update(qx, mid + 1, r, rson, val);
    pushup(p);
}

int query(int ql, int qr, int l, int r, int p)
{
    if (ql <= l && r <= qr)
        return tree[p];
    pushdown(p);
    int ans = 0;
    if (ql <= mid)
        ans = query(ql, qr, l, mid, lson);
    if (mid < qr)
        ans = max(ans, query(ql, qr, mid + 1, r, rson));
    return ans;
}

void solve(int l, int r)
{
    if (l == r)
        return (void)(dp[l] = max(dp[l], 1));
    solve(l, mid);
    for (int i = l; i <= r; i++)
        idx[i] = i;
    sort(idx + l, idx + mid + 1, compareBySequenceVal);
    sort(idx + mid + 1, idx + 1 + r, compareByMinimalVal);
    tag[1] = true, tree[1] = 0;

    int lft = l;
    for (int rgt = mid + 1; rgt <= r; rgt++)
    {
        while (lft <= mid && seq[idx[lft]] <= low[idx[rgt]])
            update(up[idx[lft]], 1, max_range, 1, dp[idx[lft]]), lft++;
        dp[idx[rgt]] = max(dp[idx[rgt]], query(1, seq[idx[rgt]], 1, max_range, 1) + 1);
    }
    solve(mid + 1, r);
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &seq[i]), up[i] = low[i] = seq[i];
    for (int i = 1, tmpx, tmpy; i <= m; i++)
    {
        scanf("%d%d", &tmpx, &tmpy);
        low[tmpx] = min(low[tmpx], tmpy), up[tmpx] = max(up[tmpx], tmpy);
        max_range = max(up[tmpx], max_range);
    }
    solve(1, n);
    int ans = 0;
    for (int i = 1; i <= n; i++)
        ans = max(ans, dp[i]);
    printf("%d", ans);
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *