1. 程式人生 > 其它 >CF1503D Flip the Cards 題解

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;
}