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;
}

 

posted @ 2021-09-03 12:32  LLTYYC  阅读(32)  评论(0编辑  收藏  举报