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)\) 对。
-
我们找到 \(a_i\) 的前驱 \(pre\) 和后继 \(nex\),\(pre < a_i < nex\)。那么 \(f_{i-1,pre,nex}+1\) 可以转移到 \(f_{i,a_i,nex},f_{i,pre,a_i}\)。
-
对于 \(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\),没有转移系数。
-
对于 \(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();
}
本文来自博客园,作者:liyixin,转载请注明原文链接:https://www.cnblogs.com/liyixin0514/p/18628587