分析:
肥肠考验分析能力的一道题目
将操作反过来看,初始状态下 ai=i a i = i 的一个序列,要求对任意满足 ap−1<ap<ap+1 a p − 1 < a p < a p + 1 的三元组,都可以翻转,求最终能否转移成给出的序列。
首先有个很显然的性质,如果以 i i 为中心做了一次翻转,那么永远不可能再以为中心做一次翻转。换言之, ai a i 的值就永远不能改变。
证明应该很显然,因为在 i i 处做了一次翻转,所以翻转后,如果在 i−1 i − 1 处做翻转,则要求 ai>ai−1 a i > a i − 1 ,在这里并不满足,因此要么改变 ai a i ,要么改变 ai−1 a i − 1 。因为能改变 ai a i 的只有在 i−1 i − 1 或 i+1 i + 1 位置翻转,因此只能尝试改变 ai−1 a i − 1 的值,但如果 ai−1 a i − 1 被改了,那么 ai−2>ai−1 a i − 2 > a i − 1 ,所以在 i−1 i − 1 位置不能反转。 i+1 i + 1 位置同理。
有了这个性质,那么问题就很容易了。
可以尝试把原序列分成若干小段 [l,r] [ l , r ] ,每一段对于 l+1,l+3,l+5……r−1 l + 1 , l + 3 , l + 5 … … r − 1 位置,满足条件 ai=i a i = i ,对于另外的位置,满足 ai≠i a i ≠ i 。方便起见,把第一类点称为不动点,第二类点称为动点。
现在继续分析性质,这次从值的角度出发,对于任意一个值 x x ,其初始位置在一个动点上,其必然满足:它如果第一步是向左转移,那么它永远不会向右走,反之亦然(即如果第一步向右转移,那么永远不会向左走)。证明很容易,如果它向左转移了,那么设初始位置为,那么转移后满足 ai<ai−1<x a i < a i − 1 < x ,如果它要向右转移,则必须令 ai−1>x a i − 1 > x ,然而 ai−1 a i − 1 是不动点,所以这永远无法满足。
并且,任意两个向左转移的元素,其相对位置不会发生变化,原因很简单,如果要发生变化,则一定交换了这两个元素,然而一旦交换,则有一个会向右转移,不合法。向右转移的元素同理。
现在总结一下性质:
首先对给出的序列分段,每一段要求
al+1=l+1,al+3=l+3……ar−1=r−1
a
l
+
1
=
l
+
1
,
a
l
+
3
=
l
+
3
…
…
a
r
−
1
=
r
−
1
同时
al≠l,al+2≠l+2……ar≠r
a
l
≠
l
,
a
l
+
2
≠
l
+
2
…
…
a
r
≠
r
a[l,r]
a
[
l
,
r
]
中所有值都在
[l,r]
[
l
,
r
]
这个区间内。
并且,所有
i<ai
i
<
a
i
的元素严格递增,所有
i>ai
i
>
a
i
的元素也严格递增。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 300010
#define INF 0x3FFFFFFF
using namespace std;
int n;
int a[MAXN];
bool used[MAXN];
bool check(int l,int r){
if(l>r)
return 0;
if(l==r)
return a[l]!=l;
int minv=INF;
int maxv=0;
for(int i=l;i<=r;i++){
minv=min(minv,a[i]);
maxv=max(maxv,a[i]);
}
if(minv!=l||maxv!=r)
return 1;
for(int i=l+1;i<=r;i+=2)
if(a[i]!=i)
return 1;
for(int i=l;i<=r;i+=2)
if(a[i]==i)
return 1;
int lasx=0,lasy=0;
for(int i=l;i<=r;i+=2){
if(i<a[i]){
if(a[i]<lasx)
return 1;
lasx=a[i];
}
if(i>a[i]){
if(a[i]<lasy)
return 1;
lasy=a[i];
}
}
return 0;
}
int main(){
SF("%d",&n);
for(int i=1;i<=n;i++)
SF("%d",&a[i]);
for(int i=1;i<=n;i++)
used[i]=(a[i]==i);
int las=1;
used[0]=1;
for(int i=1;i<=n;i++){
if(used[i]==1&&used[i-1]==1){
if(check(las,i-2)){
PF("No");
return 0;
}
las=i+1;
}
else if(used[i]==1&&used[i-2]==1)
continue;
else if(used[i]==1&&used[i-3]==1){
if(check(las,i-2)){
PF("No");
return 0;
}
las=i-1;
}
else if(used[i]==1){
PF("No");
return 0;
}
}
if(check(las,n))
PF("No");
else
PF("Yes");
}