[atARC102F]Revenge of BBuBBBlesort

定义以$i$为中心(交换$p_{i-1}$和$p_{i+1}$)的操作为操作$i$

结论1:若执行过操作$i$,则之后任意时刻都无法执行操作$i-1$或操作$i+1$

当执行操作$i$后,必然有$p_{i-1}<p_{i}$,然后不妨假设下一次与$i$相邻的操作为$i-1$($i+1$类似)

操作$i-1$的条件为$p_{i-2}>p_{i-1}>p_{i}$,即要增大$p_{i-1}$或减小$p_{i}$,可以执行操作$i-2$或操作$i+1$,后者根据假设不成立

对于操作$i-2$,其执行完后必然有$p_{i-2}<p_{i-1}$,类似的我们又要求执行操作$i-3$来增大$p_{i-2}$……以此类推,当需要执行操作1来增大$p_{2}$即不合法

推论1:执行操作$i$必须有$p_{i}=i$

根据结论1,执行操作$i$后无法再执行操作$i-1$或$i+1$,即无法再修改$p_{i}$,而最终要求$p_{i}=i$

推论2:若初始$p_{i} e i$,则一定不会执行操作$i$;若$p_{i}=i$,则一定不会执行操作$i-1$或操作$i+1$

根据推论1,执行操作$i$需要$p_{i}=i$,而修改$p_{i}$必然执行了操作$i-1$或$i+1$,则不能执行操作$i$

根据推论1,执行操作$i-1$($i+1$类似)要求$p_{i-1}=i-1<p_{i}$,不成立

考虑将整个序列划分为独立的若干段,即要求每一段的端点都不能操作,这等价于将其在$i$和$i+1$之间断开则要求$i$和$i+1$都无法操作,不妨将所有这些位置都断开,考虑每一段的形式:

1.那么每一段必然不会出现相邻两数$i$和$i+1$使得$[p_{i}=i]=[p_{i+1}=i+1]$,否则必然可以断开

2.如果端点满足$p_{i}=i$(以下假设为左端点),则$i$无法操作,同时$i+1$也无法操作,可以断开(除非$i+1$不在该段中,即仅一个$p_{i}=i$)

更形象的,记$s_{i}=[p_{i}=i]$(一个01串),那么最终每一段的$s$分为两种:1.仅一个'1';2.以'0'为开头结尾的'01010...10'

对于第1种段直接删去即可,对于第2种继续分析

可以看作对$p_{l},p_{l+2},...,p_{r}$这一段排序,而要交换$p_{i}$和$p_{i+2}$,则要求$p_{i}>i+1>p_{i+2}$

由于$p_{i}equiv i(mod 2)$,因此$p_{i}>i+1$也即$p_{i}>i$,类似的后者也即$i+2>p_{i+2}$

对于初始$p_{i}>i$的位置,其一定只能向后交换,且若交换至$p_{i}=i$,根据推论2,一定不会再改变($p_{i}<i$的位置类似)

其限制且仅限制(若两点都满足交换方向一定可行)了每一个数的交换方向,那么合法即要求:

1.如果是区间$[l,r]$,$forall lle ile r,lle p_{i}le r$

2.对于$p_{i}>i$的位置,其之前不存在比其大的数;对于$p_{i}<i$的位置,其之后不存在比其小的数

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 300005
 4 int n,a[N],p[N];
 5 int main(){
 6     scanf("%d",&n);
 7     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
 8     for(int i=1;i<=n;i++)p[i]=1;
 9     for(int i=1;i<=n;i++)
10         if (a[i]!=i)p[i]=0;
11         else p[i-1]=p[i+1]=0;
12     for(int i=1,j=1;i<=n;i=++j){
13         for(;j<n;j++)
14             if ((!p[j])&&(!p[j+1]))break;
15         if ((i==j)&&(p[i]==i))continue;
16         for(int k=i;k<=j;k++)
17             if ((a[k]<i)||(j<a[k])){
18                 printf("No");
19                 return 0;
20             }
21         int mx=0,mn=n;
22         for(int k=i;k<=j;k++){
23             if ((a[k]>k)&&(a[k]<mx)){
24                 printf("No");
25                 return 0;
26             }
27             mx=max(mx,a[k]);
28         }
29         for(int k=j;k>=i;k--){
30             if ((a[k]<k)&&(a[k]>mn)){
31                 printf("No");
32                 return 0;
33             }
34             mn=min(mn,a[k]);
35         }
36     }
37     printf("Yes");
38 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/13957452.html