F:Revenge of BBuBBBlesort!
题意:
给你一个长度为(n)的排列(A),可以交换((a_i,a_{i+1},a_{i+2}))当且仅当(a_i>a_{i-1}>a_{i-2}),问(A)是否能经过一系列交换最后有序
(1 le n le 300000)
题解:
emmmm有大佬看错了题,于是就变成了毒瘤题
真是道神仙题。。看了题解才会,证明容易构造困难啊。
先构造(B_i=[A_i=i])
有这个结论
如果B中有3个连续的0,则没有构造方案((1))
定义一个序列是合法的,当其01交错且两端是0,特殊地,单独的0,1也是合法的
我们先极大化每个位置所在合法序列的长度,然后如果对于所有合法序列同时满足以下条件,则存在方案,对于一个([l,r])
1.其中元素的值域也在([l,r]),要不然换不出去的
2.对于目标在左边的元素,对于任意的(i < j)满足(a_i < a_j) ((2))
3.对于目标在右边的元素,对于任意的(i < j)满足(a_i < a_j) ((2))
以下是口胡的证明:
((1)):
记这三个的位置是((i-1,i,i+1))如果以(i)为中心转了,那么(i)就动不了了,(i-1),(i+1)同理
((2)):
这两个东西原理一样,一起证掉,按目标在左边的考虑
如果有一个 (i <j),但(a_i > a_j),那么(a_j)一定要跨过(a_i),但这显然不太可能。。
过程:
1A
代码:
#define GG {puts("No"); return 0;}
const int N=300010;
int n;
int a[N],b[N];
inline bool Check(int l,int r) {
int lbd=0,rbd=0;
for(int i=l;i<=r;i++) {
if(b[i]<i) {
// printf("%d
",i);
if(lbd>b[i] || (b[i]<l || b[i]>r)) return false;
lbd=b[i];
} else if(b[i]>i) {
if(rbd>b[i] || (b[i]<l || b[i]>r)) return false;
rbd=b[i];
}
}
return true;
}
signed main() {
read(n);
for(int i=1;i<=n;i++)
read(b[i]),a[i]=(b[i]==i);
for(int i=2;i<n;i++) if(!a[i-1] && !a[i] && !a[i+1]) GG
for(int i=1;i<=n;i++) {
if(a[i]==0) {
int now=1;
int l=i,r=i;
while((a[r]^now) && r<=n) ++r,now^=1;
--r;
if(!Check(l,r)) GG
i=r;
}
}
puts("Yes");
return 0;
}
用时:20min