Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)B. Take Your Places!
首先显然只要保留原数列的奇偶性,所以转化成 $01$ 数列
分类讨论最终情况
如果最终为 $010101...$ 的形式
直接贪心地想,第一个位置的 $0$ 肯定要从右边最近的位置交换过来(反证法易证其最优性)
后面每个位置都是同理,要找到更后面最近的(因为前面已经处理完了)
所以直接模拟这个贪心的过程即可,就是要注意代码实现的细节
当然,从右边最近的位置交换过来时,别真的一个个相邻的交换,这个一步即可模拟(代码里会解释)
如果贪心到一半发现找不到需要的 $0$ 或 $1$ 则说明无解
如果最终为 $1010..$ 的形式
也是同理处理,把原数列取反进行一遍完全一样的操作即可
(这一题各种位运算真是烦死人了)
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; const int N=2e5+7; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int t,n,a[N],b[N]; int work() { int l=1,r=2,ans=0; //l是当前处理到的位置,r是找需要的数的指针 while(l<=n) { if( a[l]^ (l&1) )//如果不满足 10101... 的情况 { while( (!(a[r]^a[l])) && r<=n ) r++;//找到后面第一个符合要求的数 if(r>n) return -1;//判断边界 swap(a[l],a[r]); ans+=r-l;//一次性交换 //因为区间 [l,r) 都是一样的数 } l++; r=max(r,l+1); } return ans; } int main() { t=read(); while(t--) { n=read(); for(int i=1;i<=n;i++) a[i]=read()&1; for(int i=1;i<=n;i++) b[i]=a[i]; int ans=work(); for(int i=1;i<=n;i++) a[i]=b[i]^1;//取反再来一遍,注意覆盖掉a数组 int anss=work(); if(ans==-1) ans=anss; else if(anss!=-1) ans=min(ans,anss); printf("%d\n",ans); } return 0; }