CF1237H Balanced Reversals

LuoyuSitfitw發表於2023-01-27

H - Balanced Reversals

  1. 首先可以將相鄰的兩個點分到一個組中

  2. 特判無解的情況:00的數量不相等或11的數量不相等

  3. 10的數量相等(此時01的數量也相等,因為知道10的數量後01的數量就確定了,\(cnt_{01}=\frac{n}{2}-cnt_{00}-cnt{11}-cnt{10}\)),可以發現這一規律:

11 00 10 01 \(\mathop{\Rightarrow}\limits^{4,6}\) 01 11 00 10,也就是說若想將某一組點放到隊首並且此時除了這一組點的順序是反的其他點的順序照舊,只需進行兩次操作,\(x-1,x+1\)(設\(x\)為當前這一組點的第一個的下標)

根據此規律,我們可以先構造出一個與所求序列剛好反過來的序列,最後再將構造出的這個序列翻一遍即可。為了節省次數,可以只處理前\(n-2\)個字元,因為保證所有所求序列中的點對在初始序列中都出現過,所以最後剩下這一點對一定就是所求序列末尾的點對(剛好對應上且順序相同),於是最後的翻轉就變成了翻轉前\(n-2\)個字元

  1. 若不相等,就先變成相等的再解決

\(bel=cnt_{01}-cnt_{10}\)

若相等,一定有\(bel_a==bel_b\),若沒有,設我們翻轉\(a\)的字首\(p\)可以使得\(bel_{a'}==bel_b\),則可以列出式子\(bel_{a'}=bel_a-2*bel_p=bel_b\ \ \ \ \Rightarrow\ \ \ \ bel_p=\frac{bel_a-bel_b}{2}\),可以透過分類\(cnt_{01}+cnt_{10}\)以及其對應的\(cnt_{01}\)\(cnt_{10}\)的奇偶來討論證明\(bel_a\)\(bel_b\)的奇偶性相同

所以,列舉\(a\)的字首求滿足條件的字首即可

但不一定是透過翻轉\(a\)來使得相等,若翻轉\(a\),需要\(|bel_a|\geqslant|bel_b|\),因為可以發現\(a\)的某一字首的\(bel\)一定是其上一個字首的\(bel\)\(\pm1/0\)得到,也就是說從\(0\)\(bel_a\)間的數(不包含0,包含\(bel_a\))一定是\(a\)的某一字首的\(bel\),在這種情況下得到的\(bel_p\)一定是從\(0\)\(bel_a\)間的某一個數(不包含0,包含\(bel_a\)),可以透過分類正負的方法證明

同理,若\(|bel_a|\leqslant|bel_b|\),則是翻轉\(b\),此時\(bel_p=\frac{bel_b-bel_a}{2}\)

若翻轉\(a\),則就是初始序列在一開始就先翻轉;若翻轉\(b\),則是初始序列所有的翻轉已完成後再翻轉

#include<bits/stdc++.h>
using namespace std;
const int N=4005;
int n,bal_a,bal_b,bal0,bal1,ans[N],cnt,flag;
string a,b;
void rever(string &a,int r){ for(int i=0;r-i>i;++i) swap(a[i],a[r-i]); }
void rs(string &a,int t){
	int now=0;
	for(int i=0;i<n;i+=2){
		if(a[i]=='0'&&a[i+1]=='1') ++now;
		if(a[i]=='1'&&a[i+1]=='0') --now;
		if(now==t){
			flag=i+2,rever(a,i+1);
			break;
		}
	}
}
int main(){
	int T;scanf("%d",&T);
	while(T--){
		cin>>a,cin>>b,cnt=flag=bal_a=bal_b=bal0=bal1=0;
		n=a.size();
		for(int i=0;i<n;i+=2){
			if(a[i]=='0'&&a[i+1]=='1') ++bal_a;
			if(a[i]=='1'&&a[i+1]=='0') --bal_a;
			if(a[i]=='0'&&a[i+1]=='0') ++bal0;
			if(a[i]=='1'&&a[i+1]=='1') ++bal1; 
			if(b[i]=='0'&&b[i+1]=='1') ++bal_b;
			if(b[i]=='1'&&b[i+1]=='0') --bal_b;
			if(b[i]=='0'&&b[i+1]=='0') --bal0;
			if(b[i]=='1'&&b[i+1]=='1') --bal1;
		}
		if(bal0||bal1){ printf("-1\n"); continue; } 
		if(bal_a-bal_b){
			if(abs(bal_a)>=abs(bal_b)) rs(a,(bal_a-bal_b)/2),ans[++cnt]=flag,flag=0;
			else rs(b,(bal_b-bal_a)/2);
		}
		for(int i=0;i<n-2;i+=2)
			for(int j=i;j<n;j+=2)
				if(b[i]==a[j]&&b[i+1]==a[j+1]){
					(j)&&(ans[++cnt]=j),ans[++cnt]=j+2;
					rever(a,j-1),rever(a,j+1);
					break;
				}
		printf("%d\n",cnt+(n-2?1:0)+(flag?1:0));
		for(int i=1;i<=cnt;++i) printf("%d ",ans[i]);
		if(n-2) printf("%d ",n-2);
		if(flag) printf("%d",flag);
		printf("\n");
	}
	return 0;
}