[单调栈+线段树] 2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest ——A. LaIS

题面$

link
  定义了长度为 k k k 的几乎递增序列 { b i } \{b_i\} {bi} 为: m i n ( b 1 , b 2 ) ≤ m i n ( b 2 , b 3 ) ≤ . . . m i n ( b k − 1 , b k ) min(b_1, b_2) ≤ min(b_2, b_3) ≤ ... min(b_{k - 1}, b_k) min(b1,b2)min(b2,b3)...min(bk1,bk)
  现在给定了一个长度为 n   ( n ≤ 5 e 5 ) n \ (n ≤ 5e5) n (n5e5) 的数组 { a i }   ( 1 ≤ a i ≤ n ) \{a_i\} \ ( 1 ≤a_i ≤ n) {ai} (1ain),求几乎递增子序列的最长长度。

分析

  我们可以得到,对于一个长度为 k k k 的几乎递增序列 { b i } ( 1 ≤ i ≤ k ) \{b_i\} (1 ≤ i ≤ k) {bi}(1ik) ,当 i ≥ 3 i ≥ 3 i3 时,有 b i ≥ m i n ( b i − 1 , b i − 2 ) b_i ≥ min(b_{i-1}, b_{i-2}) bimin(bi1,bi2) 成立,即要么大于等于它前一个数,要么大于等于其前前一个数。如果我们定义了 b 0 = 0 b_0 = 0 b0=0,那么对于 i ≥ 2 i ≥ 2 i2 都成立。
  如果定义以 b i b_i bi 结尾的几乎递增序列的最大长度为 d p [ i ] dp[i] dp[i] ,则有三种转移情况:
  ①为一个子序列的首部,即 d p [ i ] = 1 dp[i] = 1 dp[i]=1;
  ②对于已有子序列 { b m 1 , b m 2 , . . . b m k } \{b_{m1}, b_{m2}, ...b_{mk}\} {bm1,bm2,...bmk},有 b i ≥ b m k b_i ≥ b_{m_k} bibmk,即大于等于最后一个数,也就是对于 j < i j < i ji, 满足 a [ j ] ≤ a [ i ] a[j] ≤ a[i] a[j]a[i],则 d p [ i ] = m a x ( d p [ j ] + 1 ) dp[i] = max(dp[j] + 1) dp[i]=max(dp[j]+1);
  ③对于已有子序列 { b m 1 , b m 2 , . . . b m k − 1 , b m k } \{b_{m1}, b_{m2}, ...b_{mk-1}, b_{mk}\} {bm1,bm2,...bmk1,bmk},若 b m k − 1 ≥ b m k b_{mk-1} ≥ b_{mk} bmk1bmk,那么肯定也能从 b m k b_{mk} bmk 转移过来,我们这里就不考虑,只考虑 b m k − 1 < b m k b_{mk-1} < b_{mk} bmk1bmk 的情况,也就是对于 j 1 < j 2 < i j_1 < j_2 < i j1j2i, 满足 a [ j 1 ] < a [ j 2 ] ,   a [ j 1 ] ≤ a [ i ] a[j_1] < a[j_2], \ a[j_1] ≤ a[i] a[j1]a[j2], a[j1]a[i],则 d p [ i ] = m a x ( d p [ j 1 ] + 2 ) dp[i] = max(dp[j_1] + 2) dp[i]=max(dp[j1]+2);
  
  对于 ①、②两种情况,由于 { a i }   ( 1 ≤ a i ≤ n ) \{a_i\} \ ( 1 ≤a_i ≤ n) {ai} (1ain),可以分别用两棵线段树来记录当前满足条件的值的最大值,如①,可以在第一棵线段树查询最后一个值在 [ 0 , a [ i ] ] [0, a[i]] [0,a[i]] 的最长几乎递增序列,然后去更新 a [ i ] a[i] a[i],对于②的话,由于需要右侧有一个数大于它,所以需要用单调栈来维护一下,当它右侧第一个大于它的数被更新后,才能更新它。

#include <bits/stdc++.h>
#include <iostream>
#include <set>
#define here printf("modassing [%s] in LINE %d\n", __FUNCTION__, __LINE__);
#define debug(x) cout << #x << ":\t" << (x) << endl;

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int maxn = 5e5 + 10;
const int maxm = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll mod = 998244353;
const double pi = acos(-1.0);

class Segment_Tree
{
    int tree[maxn << 2];

    public:
    void build(int node, int l, int r)
    {
        if(l == r)
        {
            tree[node] = -INF;
            return;
        }
        int mid =  (l + r) / 2;
        build(node * 2, l, mid);
        build(node * 2 + 1, mid + 1, r);
        tree[node] = -INF;
    }

    int query(int node, int l, int r, int x, int y)
    {
        if(x <= l && y >= r)
            return tree[node];
        int ans = -INF, mid = (l + r) / 2;
        if(x <= mid) ans = max(query(node * 2, l, mid, x, y), ans);
        if(y > mid) ans = max(query(node * 2 + 1, mid + 1, r, x, y), ans);
        return ans;
    }

    void update(int node, int l, int r, int x, int c)
    {
        if(l == r)
        {
            tree[node] = max(tree[node], c);
            return;
        }
        int mid = (l + r) / 2;
        if(x <= mid) update(2*node, l, mid, x, c);
        else update(2 * node + 1, mid + 1, r, x, c);
        tree[node] = max(tree[node * 2], tree[node * 2 + 1]);
    }
}t1, t2;

struct node
{
    int id, nxt;
    bool operator<(const node& m)
    {
        return nxt < m.nxt;
    }
} pro[maxn];

int t, n, a[maxn], dp[maxn];

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++) pro[i].id = i;

        stack<P> st;
        for (int i = 1; i <= n; i++)
        {
            while(!st.empty() && a[i] >= st.top().first)
            {
                pro[st.top().second].nxt = i;
                st.pop();
            }
            st.push(P(a[i], i));
        }
        while(!st.empty())
        {
            pro[st.top().second].nxt = n + 1;
            st.pop();
        }
        //for (int i = 1; i <= n; i++)
            //cout << pro[i].nxt << endl;
            sort(pro + 1, pro + 1 + n);

        int cur = 0;
        t1.build(1, 0, n);
        t2.build(1, 0, n);
        t1.update(1, 0, n, 0, 0);
        for (int i = 1; i <= n; i++)
        {
            dp[i] = max({1, t1.query(1, 0, n, 0, a[i]) + 1, t2.query(1, 0, n, 0, a[i]) + 2});
             //cout << i << " " << dp[i] << endl;
            while(cur < n && pro[cur].nxt <= i)
            {
                t2.update(1, 0, n, a[pro[cur].id], dp[pro[cur].id]);
                cur++;
            }
            t1.update(1, 0, n, a[i], dp[i]);
        }

        int ans = 1;
        for (int i = 1; i <= n; i++)  ans = max(ans, dp[i]);
        printf("%d\n", ans);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值