题解 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\) 个数不取反 / 取反后作为其中一个上升子序列最大值出现时,另一个上升子序列的最大值的最小值。

\[f_{i,0}=\min\begin{cases} f_{i-1,0},&(a_{i-1}<a_i),\\ f_{i-1,1},&(-a_{i-1}<a_i),\\ a_{i-1},&(f_{i-1,0}<a_i),\\ -a_{i-1},&(f_{i-1,1}<a_i). \end{cases}\]

如果是任意解,那么随意 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;
}
posted @ 2022-11-10 16:12  caijianhong  阅读(20)  评论(0编辑  收藏  举报