2022.7.20 AGC052D&P4338&AGC033E

AGC052D Equal LIS

题意:给定一个排列,将其划分为两个子序列,使得 LIS 长度相等,判断可行性。

题解:设 LIS 长度为 \(L\),若 \(L=2k\),显然我们将 \(f_i\leqslant k\) 的分一边,\(f_i\geqslant k\) 的分一边就好。

\(L=2k+1\),若我们能找到一个 \(x\) 使得去掉 \(x\) 后 LIS 仍为 \(L\) 且存在一个包含 \(x\) 的长度为 \(k+1\) 的上升子序列即可,我们证明它是充要的。

假设上升子序列为 \(\{p_1,\cdots,p_{k+1}\}\),构造第一个序列 \(a=\{i|f_i\neq f_{p_j} \or (f_i=f_x,i\neq x)\}\),第二个序列为其补集。

第一个序列就是说,它放进去的 \(f\) 总共只有 \(k+1\) 种,所以答案不超过 \(k+1\);其次,这 \(k+1\) 种除了 \(x\) 全在这了,且 \(x\) 不在 LIS 里,所以一定存在一个长度为 \(k+1\) 的 LIS 的子序列。另一个序列就是 \(f\) 也只有 \(k+1\) 种,所以答案不大于 \(k+1\);其次,我们选出的 \(k+1\)\(p\) 序列全在里面。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int T,n,m,f[maxn],g[maxn],tr[maxn],p[maxn];
inline int query(int x){int res=0;for(;x;x-=x&(-x))res=max(res,tr[x]);return res;}
inline void upd(int x,int y){for(;x<=n;x+=x&(-x))tr[x]=max(tr[x],y);}
inline void solve(){
	n=read();int L=0,cnt=0;f[0]=g[n+1]=0;
	for(int i=0;i<=n+1;i++)f[i]=g[i]=tr[i]=0;
	for(int i=1;i<=n;i++)p[i]=read();
	for(int i=1;i<=n;i++)f[i]=1+query(p[i]),upd(p[i],f[i]);
	for(int i=1;i<=n;i++)tr[i]=0;
	for(int i=n;i>=1;i--)g[i]=1+query(n+1-p[i]),upd(n+1-p[i],g[i]);
	for(int i=1;i<=n;i++)L=max(L,f[i]);
	if(L%2==0)return void(puts("YES"));
	for(int i=1;i<=n;i++)cnt+=(f[i]+g[i]-1>=L/2+1);
	puts(cnt>L?"YES":"NO");
}
int main(){
	T=read();
	while(T--)solve();
	return 0;
}
posted @ 2022-07-29 01:26  syzf2222  阅读(73)  评论(0编辑  收藏  举报