Dilworth 定理 | AGC052D Equal LIS
模拟赛考了这题的加强版,我被肘飞了。模拟赛那题除了要判定是否合法之外,还要求构造。感觉还是有迹可循的啊。
这题一个很重要的 Trick 就是 LIS 的分层,我们设 \(f_i\) 表示以 \(p_i\) 结尾的 LIS 的长度。然后按照 \(f_i\) 进行分组,值相同的在一组。可以发现 \(f_i\) 相同的元素内部不会形成 LIS,它们的结构形如若干逆序对,如果是顺序对的话,就会有一个 \(f\) 值更新另外一个导致它们的 \(f\) 值不同。
本质是 Dilworth 定理,紫色的为最长链。淡蓝色的若干反链,同一条淡蓝色线上的 \(f\) 值相同。

令 LIS 长度为 \(2k/2k+1\)。
这个时候我们就可以轻松解决 LIS 为偶数的情况了,因为这样子的蓝线个数也为偶数,随便选 \(k\) 组放在一起,另外 \(k\) 组放在一起。由于组内不会产生贡献,所有两个部分的 LIS 都是 \(k\)。
对于 LIS 为 \(2k+1\) 的情况有点难做,考虑让两组的长度都是 \(k+1\)。可是我们只能划分出 \(k\) 组合上 \(k+1\) 的情况。于是考虑“借”一个元素来完成这个构造,如果如图深蓝色部分的选择,我们选择 \(k\) 条浅蓝色的链的全部元素,然后借用另一条浅蓝色线上的一个元素就行了,这样子这一个子序列的 LIS 为 \(k+1\),另一个子序列由于包含了 \(k+1\) 组,所以 LIS 也为 \(k+1\)。
选择的这个元素必须满足其所在组的大小 \(>1\)(否则我们取完这个元素之后,这个组就空了,没法贡献给另外一个子序列了),且存在一个长度为 \(k+1\) 的上升子序列包含它。当然如果不存在这个元素那就无解了。
使用树状数组维护 LIS,时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb emplace_back
#define mp make_pair
using namespace std;
const int maxn=2e5+10;
void cmin(int &x,int y){ x=x<y?x:y; }
void cmax(int &x,int y){ x=x>y?x:y; }
vector<int> vec; int ban[maxn];
int n,vis[maxn],p[maxn],f[maxn],g[maxn],cnt[maxn];
struct BIT{
int c[maxn];
int lowbit(int x){ return x&-x; }
void init(){ for(int i=1;i<=n;i++) c[i]=0; }
void modify(int x,int v){ for(;x<=n;x+=lowbit(x)) cmax(c[x],v); }
int query(int x){ int res=0; for(;x;x-=lowbit(x)) cmax(res,c[x]); return res; }
}t;
void init(){ for(int i=1;i<=n;i++) vis[i]=cnt[i]=ban[i]=0; }
void solve(){
cin>>n; init(); t.init(); int len=0;
for(int i=1;i<=n;i++){
cin>>p[i];
f[i]=t.query(p[i])+1;
t.modify(p[i],f[i]);
cmax(len,f[i]);
}
if(len%2==0){
cout<<"YES"<<endl;
for(int i=1;i<=n;i++)
if(f[i]<=len/2) cout<<0<<" ";
else cout<<1<<" "; cout<<endl;
return ;
}
t.init(); int k=(len+1)/2;
for(int i=n;i>=1;i--){
g[i]=t.query(n-p[i]+1)+1;
t.modify(n-p[i]+1,g[i]);
}
for(int i=1;i<=n;i++)
if(f[i]+g[i]-1==len) cnt[f[i]]++;
int id=0;
for(int i=1;i<=n;i++){
if(f[i]+g[i]-1==len&&cnt[f[i]]==1) continue;
if(f[i]+g[i]-1>=k) id=i;
}
if(!id){ cout<<"NO"<<endl; return ; }
cout<<"YES"<<endl;
int L=f[id]-min(f[id],k)+1,R=g[id]-(k-min(f[id],k));
ban[f[id]]=1;
int cur=f[id]-1; vis[id]=1;
for(int i=id-1;i>=1&&cur>=L;i--)
if(f[i]==cur) cur--,vis[i]=1,ban[f[i]]=1;
cur=g[id]-1;
for(int i=id+1;i<=n&&cur>=R;i++)
if(g[i]==cur) cur--,vis[i]=1,ban[f[i]]=1;
for(int i=1;i<=n;i++)
if(vis[i]) cout<<0<<" ";
else if(!ban[f[i]]) cout<<1<<" ";
else if(f[i]==f[id]) cout<<1<<" ";
else cout<<0<<" "; cout<<endl;
}
int main(){
int T; cin>>T;
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号