Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)B. Take Your Places!
阿新 • • 發佈:2021-09-03
首先顯然只要保留原數列的奇偶性,所以轉化成 $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; }