【AT4363】[ARC102D] Revenge of BBuBBBlesort!(结论题)

点此看题面

  • 给定一个长度为\(n\)的排列,一次操作可以选择一个\(i\)满足\(p_{i-1}>p_i>p_{i+1}\),然后交换\(p_{i-1}\)和\(p_{i+1}\)。
  • 要求判断是否能把这个排列转化为\(1,2,...,n\)。
  • \(n\le3\times10^5\)

重要结论

我们只可能选择一开始就满足\(p_i=i\)的位置操作,证明就考虑反证。

对于一个\(p_i\not=i\)的位置,如果我们操作了它,就会导致\(p_{i-1}<p_i<p_{i+1}\)。

以\(p_i<i\)的情况为例,我们想要把它向左移,就必须操作\(i-2\)使得\(p_{i-1}>p_i\),那么就必然导致\(p_{i-2}<p_{i-1}\),因此不可能再把\(i\)往左移了,必然无解。

对于一个若干次操作后\(p_i=i\)的位置,则这个位置必然是跟\(p_{i-2}\)或\(p_{i+2}\)交换来的。

若跟\(p_{i-2}\)交换来则\(p_i>p_{i-1}\),若跟\(p_{i+2}\)交换来则\(p_i<p_{i+1}\),不可能满足\(p_{i-1}<p_i<p_{i+1}\),无法操作。

区间划分+推结论

根据先前的结论,如果有三个连续位置都满足\(p_i\not=i\),那么中间的位置始终无法被交换到,肯定无解。

否则,我们可以把连续一段\(01010...010\)取出来,当做一个独立的区间处理。

考虑判断一个区间\([l,r]\)是否有解,首先一个必要条件就是所有数都必须在\([l,r]\)范围内。

然后我们还需要一个条件,就是仅保留\(p_i\not=i\)的位置时最长下降子序列长度不能超过\(2\)。

首先这是必要的,因为存在一个长度为\(3\)的下降子序列时,肯定无法让它们全部到自己位置上。

其实这是充分的,因为一次交换让相邻两个元素变得有序,不可能使得最长下降子序列变大,那么当它长度变为\(1\)时就达成了条件。

代码:\(O(n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300000
using namespace std;
int n,p[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int f[2];I void Solve(CI l,CI r)
{
	#define NA() (puts("No"),exit(0),0)//无解
	if(l>r) return;RI i,Mn=n,Mx=1;for(i=l;i<=r;i+=2) Mn=min(Mn,p[i]),Mx=max(Mx,p[i]);(Mn^l||Mx^r)&&NA();//如果值不全在[l,r]范围内无解
	for(f[0]=f[1]=0,i=l;i<=r;i+=2) p[i]>f[0]?f[0]=p[i]:(p[i]>f[1]?f[1]=p[i]:NA());//维护最长下降子序列判断长度是否会超过2
}
int main()
{
	RI i,j;for(read(n),i=1;i<=n;++i) read(p[i]);
	for(i=1;i+2<=n;++i) if(p[i]^i&&p[i+1]^(i+1)&&p[i+2]^(i+2)) return puts("No"),0;//连续三个p[i]≠i无解
	for(i=1;i<n;++i) if(p[i]^i&&p[i+1]==i+1) {for(j=i;i+2<=n&&p[i+1]==i+1&&p[i+2]^(i+2);i+=2);Solve(j,i);}//抠出每段区间处理
	return puts("Yes"),0;
}
上一篇:[NOI2020] 制作菜品


下一篇:从零开始的图论