CF1503D Flip the Cards 题解

Codeforces
Luogu

Description.

\(n\) 张牌,正面反面都写着两两不同的数。
你可以翻转若干张牌,然后任意排序。
要求正面的数构成单调递增序列,翻面构成单调递减序列。
问最少翻转次数,或判断无解。

Solution.

先考虑无解情况,如果存在一对 \(a_i\le a_j,b_i\le b_j\) 肯定无解。
所以肯定不存在 \(a_i\le n,b_i\le n\),这样肯定会有一个 \(a_i>n,b_i>n\),就无解了。
所以相当于如果有解肯定是 \(a_i\in[1,n],b_i\in[n+1,n]\)
我们可以考虑把 \(a_i(a_i\le n) \rightarrow b_i(b_i>n)\),建立映射关系 \(f\)
相当于我们要把 \(f_i\) 划分成两个递减子序列,次数最小。

考虑找到前缀 \(\min\) 和后缀 \(\max\)
如果 \(\min_x>\max_{x+1}\),那在这边断开肯定是可行的且是最优的。
如果不存在 \(\min_x>\max_{x+1}\),那相当于后面肯定有一个比当前大,那当前肯定塞到最小的那一个子序列里去,这样是唯一的。

Coding.

点击查看代码
//Coded by leapfrog on 2021.10.29 {{{
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
const int N=400005;int n,a[N],b[N],qz[N],hz[N],fg[N],f[N];
int main()
{
	read(n);for(int i=1;i<=n;i++) read(a[i],b[i]);
	for(int i=1;i<=n;i++) if(a[i]<=n&&b[i]<=n) return puts("-1"),0;
	for(int i=1;i<=n;i++) fg[min(a[i],b[i])]=a[i]>b[i]&&(swap(a[i],b[i]),1),f[a[i]]=b[i];
	qz[0]=1e9;for(int i=1;i<=n;i++) qz[i]=min(qz[i-1],f[i]);
	for(int i=n;i>=1;i--) hz[i]=max(hz[i+1],f[i]);
	int ta=1e9,tb=1e9,sa=0,sb=0,ca=0,cb=0,rs=0;
	for(int i=1;i<=n;i++)
	{
		if(ta>tb) swap(ta,tb),swap(sa,sb),swap(ca,cb);
		if(f[i]<ta) ta=f[i],sa++,ca+=fg[i];
		else if(f[i]<tb) tb=f[i],sb++,cb+=fg[i];
		else return puts("-1"),0;
		if(qz[i]>hz[i+1]) rs+=min(ca+sb-cb,cb+sa-ca),ta=tb=1e9,sa=sb=ca=cb=0;
	}return printf("%d\n",rs),0;
}
posted @ 2021-10-29 15:08  Peal_Frog  阅读(80)  评论(0编辑  收藏  举报