题解 CF1620F【Bipartite Array】
problem
将一个排列中所有逆序对连起来,如果连出来一个二分图,我们说它是一个二分图排列。
给一个排列 \(a\),可以选择若干个 \(i\) 使得 \(a_i\gets-a_i\)(取相反数),请把 \(a\) 变成字典序最小的二分图排列,或者报告无解。\(n\leq 10^6\)。
solution 0
结论一:若排列 \(a\) 是二分图排列,当且仅当不存在 \(i<j<k\) 使得 \(a_i>a_j>a_k\)。
证明:如果存在,说明这个二分图竟然有奇环。
结论二:若排列 \(a\) 是二分图排列,当且仅当 \(a\) 能被(不连续地)划分成两个单调升的子序列。
证明:还记得 Dilworth 定理吗?如果被划分成三个,那么 \(a\) 的最长下降子序列长度为 \(3\)。
暴力 dfs 会吧?
solution 1
DP。令 \(f_{i,j,k}\) 表示前缀 \([1,i]\),一个上升子序列的最大值(最后一个)是 \(j\),一个是 \(k\),是否合法。有点暴力了。
观察:
- \(|j|,|k|\) 的其中之一等于 \(a_{i-1}\)。
- 如果 \(f_{i,j,k}\) 合法,那么 \(f_{i,j,k'\geq k}\)(在某种意义上)也会合法。
改一下:\(f_{i,0/1}\) 表示第 \(i\) 个数不取反 / 取反后作为其中一个上升子序列最大值出现时,另一个上升子序列的最大值的最小值。
如果是任意解,那么随意 DP 随意记录前驱就做完了。复杂度线性。
solution 2
注意我们需要字典序最小,因此要倒着 DP(因为字典序优先比较前面的),将所有 \(\min\) 改成 \(\max\) 之后,要贪心构造解。
具体地,动态维护两个子序列的末尾为 \(l_1,l_2\),钦定 \(l_1<l_2\),从小到大考虑:
- 若 \(l_2<-a_i\) 将 \(-a_i\) 放入 \(l_2\)。
- 若 \(l_1<-a_i\),同时 \(l_2<f_{i,0}\)(满足另外一个子序列的限制,大了可能会寄),将 \(-a_i\) 放入 \(l_1\)。
- 否则将 \(a_1\) 放入 \(l_2\)。
复杂度线性。
code
已与 std 对拍 2e8 组数据,应该没挂。
// LUOGU_RID: 93510947
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#undef LOCAL
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int n,a[1000010];
pair<int,int> f[1000010][2];
void dp(){
f[n][0]=f[n][1]={1e9,0};
for(int i=n-1;i>=1;i--){
f[i][0]=f[i][1]={-1e9,0};
debug("i=%d\n",i);
if(a[i+1]>a[i]) f[i][0]=max(f[i][0],{f[i+1][0].first,0}),debug("line %d\n",__LINE__);
if(-a[i+1]>a[i]) f[i][0]=max(f[i][0],{f[i+1][1].first,1}),debug("line %d\n",__LINE__);
if(f[i+1][0].first>a[i]) f[i][0]=max(f[i][0],{a[i+1],0}),debug("line %d\n",__LINE__);
if(f[i+1][1].first>a[i]) f[i][0]=max(f[i][0],{-a[i+1],1}),debug("line %d\n",__LINE__);
if(a[i+1]>-a[i]) f[i][1]=max(f[i][1],{f[i+1][0].first,0}),debug("line %d\n",__LINE__);
if(-a[i+1]>-a[i]) f[i][1]=max(f[i][1],{f[i+1][1].first,1}),debug("line %d\n",__LINE__);
if(f[i+1][0].first>-a[i]) f[i][1]=max(f[i][1],{a[i+1],0}),debug("line %d\n",__LINE__);
if(f[i+1][1].first>-a[i]) f[i][1]=max(f[i][1],{-a[i+1],1}),debug("line %d\n",__LINE__);
}
}
void solve(int,int){
int l1=-1e9,l2=-1e9;
for(int i=1;i<=n;i++){
if(-a[i]>l2) printf("%d%c",l2=-a[i]," \n"[i==n]);
else if(-a[i]>l1&&l2<f[i][1].first) printf("%d%c",l1=-a[i]," \n"[i==n]);
else printf("%d%c",l2=a[i]," \n"[i==n]);
}
}
int mian(){
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
if(n==1) return printf("YES\n-%d\n",a[1]),0;
dp();
for(int i=n;i>=1;i--) debug("i=%d,f[%d][0]={%d,%d},f[%d][1]={%d,%d}\n",i,i,f[i][0].first,f[i][0].second,i,f[i][1].first,f[i][1].second);
if(f[1][1].first>-1e9) puts("YES"),solve(1,1);
else if(f[1][0].first>-1e9) puts("YES"),solve(1,0);
else puts("NO");
return 0;
}
void reset(){
}
int main(){
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
for(scanf("%*d");~scanf("%d",&n);reset(),mian());
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-CF1620F.html