分析:
肥肠考验分析能力的一道题目
将操作反过来看,初始状态下 的一个序列,要求对任意满足 的三元组,都可以翻转,求最终能否转移成给出的序列。
首先有个很显然的性质,如果以 为中心做了一次翻转,那么永远不可能再以 为中心做一次翻转。换言之, 的值就永远不能改变。
证明应该很显然,因为在 处做了一次翻转,所以翻转后 ,如果在 处做翻转,则要求 ,在这里并不满足,因此要么改变 ,要么改变 。因为能改变 的只有在 或 位置翻转,因此只能尝试改变 的值,但如果 被改了,那么 ,所以在 位置不能反转。 位置同理。
有了这个性质,那么问题就很容易了。
可以尝试把原序列分成若干小段 ,每一段对于 位置,满足条件 ,对于另外的位置,满足 。方便起见,把第一类点称为不动点,第二类点称为动点。
现在继续分析性质,这次从值的角度出发,对于任意一个值 ,其初始位置在一个动点上,其必然满足:它如果第一步是向左转移,那么它永远不会向右走,反之亦然(即如果第一步向右转移,那么永远不会向左走)。证明很容易,如果它向左转移了,那么设初始位置为 ,那么转移后满足 ,如果它要向右转移,则必须令 ,然而 是不动点,所以这永远无法满足。
并且,任意两个向左转移的元素,其相对位置不会发生变化,原因很简单,如果要发生变化,则一定交换了这两个元素,然而一旦交换,则有一个会向右转移,不合法。向右转移的元素同理。
现在总结一下性质:
首先对给出的序列分段,每一段要求
同时
中所有值都在
这个区间内。
并且,所有
的元素严格递增,所有
的元素也严格递增。
#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");
}