给定长为 (n) 的排列,求是否可以分割成两个 LIS 长度相等的子序列。
(sum nle 2cdot 10^5)。
设 (f_i) 是以 (i) 为结尾的 LIS 长度,(L=max_{i=1}^nf_i) 是整体 LIS 长度。
当 (2mid L) 时一定有解,划分为 (f_ilefrac L2) 和 (f_i>frac L2) 两部分即可。
当 (L=2k+1) 为奇数时,两个子序列的 LIS 长度至少是 (k+1),所以必定存在某个 LIS 之外的元素,存在某个长为 (k+1) 的 IS 覆盖它。
事实证明这是充分的所以做完了,考虑构造:设这个长为 (k+1) 的 IS 的位置序列是 (p_1,cdots,p_{k+1}),将满足 (forall jin[1,k+1],f_i
e f_{p_j}) 或 (f_i=f_x) 且 (i
e x) 的 (i) 划分为第一个序列,其他的划分为第二个序列。
显然两个序列分别至多有 (k+1) 个不同的 (f),所以 LIS 长度 (le k+1)。
在第一个序列中,对于长为 (L) 的 LIS,其中恰有 (k) 个值不能选,所以 LIS 长度 (ge k+1)。
在第二个序列中,(p) 是其子序列,所以 LIS 长度 (ge k+1)。
所以再求出 (g_i) 表示以 (i) 为开头的 LIS 长度即可,时间复杂度 (O(nlog n))。
#include<bits/stdc++.h>
using namespace std;
const int N = 200003;
template<typename T>
void read(T &x){
int ch = getchar(); x = 0;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int T, n, p[N], tr[N], f[N], g[N];
void upd(int p, int v){for(;p <= n;p += p & -p) chmax(tr[p], v);}
int qry(int p){int r = 0; for(;p;p -= p & -p) chmax(r, tr[p]); return r;}
void solve(){
read(n); int ans = 0;
memset(tr, 0, n+1<<2);
for(int i = 1;i <= n;++ i){
read(p[i]);
upd(p[i], f[i] = qry(p[i]-1) + 1);
chmax(ans, f[i]);
}
if(!(ans & 1)){puts("YES"); return;}
memset(tr, 0, n+1<<2);
for(int i = n, j = ans;i;-- i){
upd(n+1-p[i], g[i] = qry(n-p[i]) + 1);
if(f[i] == j) -- j;
else if(f[i] + g[i] > (ans>>1) + 1){
puts("YES"); return;
}
}
puts("NO");
}
int main(){read(T); while(T --) solve();}