minmax

minmax

题意

给你一个长度为 \(n\) 的排列 \(A\),你要把它拆成两个子序列 \(B,C\),使得 \(B\)前缀最大值的个数加上 \(C\)前缀最小值的个数最大。

\(n \le 2 \times 10^5\)

思路

考虑 DP。

\(f_{i,x,y}\) 表示考虑到 \(a_i\)\(B\) 中前缀最大值是 \(x\)\(C\) 中前缀最大值是 \(y\) 的最大答案。显然这个状态定义是容易转移的。对每个 \(a_i\) 分讨放在哪个序列里,最暴力的复杂度是 \(n^3\)

考虑一些优化。

对于 \(f_{i-1,x,y}\) 的转移:

  • \(x>y\)

    无论 \(a_i\) 是多少,如果我们不想用它,一定可以放到 \(B,C\) 其中一个序列里面使得 \(a_i\)\(A\) 的前缀最大值和 \(B\) 的后缀最小值都没有影响。因此我们只需要对 \([i,n]\)\(>x\) 的最长上升子序列(LIS)和 \(<y\) 的最长下降子序列(LDS)即可。

  • \(x<y\)

    这说明在 \([1,i-1]\) 中所有 \(\le x\) 的数字都在 \(B\) 中,剩下的即 \(\ge y\) 的都在 \(C\) 中。也就是说,\(x,y\) 是大小相邻的两个数,那么 \((x,y)\) 只有 \(O(n)\) 对。

    1. 我们找到 \(a_i\) 的前驱 \(pre\) 和后继 \(nex\)\(pre < a_i < nex\)。那么 \(f_{i-1,pre,nex}+1\) 可以转移到 \(f_{i,a_i,nex},f_{i,pre,a_i}\)

    2. 对于 \(y<a_i\) 的,把 \(a_i\) 放在 \(B\)\(f_{i-1,x,y}\) 可以转移到 \(f_{i,a_i,y}\),获得 \(1\) 的转移系数,并且由于 \(a_i>y\),无需记录这个状态,直接算 LIS 和 LDS 然后计入答案。也可以把 \(a_i\) 放在 \(C\),没有转移系数。

    3. 对于 \(x>a_i\) 的,把 \(a_i\) 放在 \(C\)\(f_{i-1,x,y}\) 可以转移到 \(f_{i,x,a_i}\),获得 \(1\) 的转移系数,并且由于 \(x>a_i\),无需记录这个状态,直接算 LIS 和 LDS 然后计入答案。也可以把 \(a_i\) 放在 \(B\),没有转移系数。

    \(1\) 种情况可以直接 set。

    \(2,3\) 种情况实际上不会改变 \(f\) 数组,只需要求最值。考虑线段树或者树状数组维护。两棵树状数组维护区间 \(f\) 加上 LIS 或者 LDS 的长度的最值。然后每次分别查询后缀 LIS 或者 LDS。

    可以先预处理出所有的 \(pre\)\(nex\),然后倒序枚举 \(i\),更新树状数组。

实际上对于情况 \(2,3\),不一定要把 \(a_i\) 放在指定序列才能计入答案。如果把 \(a_i\) 扔掉,直接求 LIS 和 LDS 也是正确的。

code

代码是好写的。参考了沈队的代码。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace ocean {
    constexpr int N=2e5+7;
    int t,n,a[N];
    struct tree {
        int tr[N];
        void clear() { memset(tr,0,sizeof(int)*(n+3)); }
        void add(int x,int y) {
            for(;x<=n;x+=x&-x) tr[x]=max(tr[x],y);
        }
        int ask(int x) {
            int s=0;
            for(;x;x-=x&-x) s=max(s,tr[x]);
            return s;
        }
    }T1,T2;
    int f[N],g[N];
    set<int> st;
    int pre[N],nex[N];
    int ans;
    void init() {
        ans=0;
        T1.clear(), T2.clear();
        memset(f,0,sizeof(int)*(n+3));
        memset(g,0,sizeof(int)*(n+3));
        st.clear();
    }
    void main() {
        sf("%d",&t);
        while(t--) {
            sf("%d",&n);
            rep(i,1,n) sf("%d",&a[i]);
            init();
            st.insert(0), st.insert(n+1);
            rep(i,1,n) {
                auto it=st.lower_bound(a[i]);
                pre[i]=*prev(it), nex[i]=*it;
                st.insert(a[i]);
                f[i]=g[a[i]]=++g[pre[i]];
            }
            per(i,n,1) {
                int s=0;
                for(int j : {pre[i],nex[i],a[i]}) {
                    if(j&&j<=n) s=max(s,T1.ask(j)+T2.ask(n-j+1));
                }
                ans=max(ans,s+f[i]);
                T1.add(a[i],T1.ask(a[i])+1), T2.add(n-a[i]+1,T2.ask(n-a[i]+1)+1);
            }
            pf("%d\n",ans);
        }
    }
}
int main() {
    #ifdef LOCAL
    freopen("my.out","w",stdout);
    #endif
    ocean :: main();
}
posted @ 2024-12-24 21:45  liyixin  阅读(9)  评论(0编辑  收藏  举报