*题解:CF1545B AquaMoon and Chess

题目链接

解析

手玩发现可以将操作转化为选定一个 \(\texttt{11}\) 往左/往右移,前提是它相邻的左侧/右侧有 \(\texttt{0}\)

进一步地,可以发现如果序列中所有 \(\texttt{1}\) 的连续段长度都是偶数,那么可以把它们划分成若干个 \(\texttt{11}\) 并任意移动。记序列中有 \(x\)\(\texttt{0}\),可以划分出 \(y\)\(\texttt{11}\),考虑插板,用 \(\texttt{11}\) 来分隔 \(\texttt{0}\),答案即为 \(C_{x + y}^{y}\)

对于 \(\texttt{1}\) 的连续段长度为奇数的情况,会有一个 \(\texttt{1}\) 无法自由移动,无论如何操作,其右侧的 \(\texttt{0}\) 的个数不变,即与 \(\texttt{0}\) 的相对位置不变,故在操作时可以将其视为一个 \(\texttt{0}\)。记有 \(z\) 个无法移动的 \(\texttt{1}\),仍然使用插板法,结果为 \(C_{x + y + z}^{y}\)。但是这样是不对的,因为对于一个无法自由移动的 \(\texttt{1}\)\(\texttt{11}\) 不管插在其相邻的左侧还是右侧都没有区别,于是可以直接把无法自由移动的 \(\texttt{1}\) 移除,最终答案为 \(C_{x + y} ^ y\)

时间复杂度 \(O(n)\)

代码

/*
*/
#include <bits/stdc++.h>
#define eps 0.0000000001
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1) 
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef unsigned ui;
typedef pair<ll, ll> pii;
const int N = 1e5 + 5, M = 20, P = 450, mod = 998244353, mod2 = 1e9 + 7, b1 = 131;
int fac[N],inv[N];
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = 1ll * res * a % mod;
		a = 1ll * a * a % mod;
		b >>= 1;
	}
	return res;
}
int C(int a,int b){
	if(a < b) return 0;
	return 1ll * fac[a] * inv[b] % mod * inv[a - b] % mod;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	fac[0] = 1;
	for(int i=1;i<N;i++){
		fac[i] = 1ll * fac[i - 1] * i % mod;
	}
	inv[N - 1] = qmi(fac[N - 1],mod - 2);
	for(int i=N - 2;i>=0;i--){
		inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
	}
	int T;
	cin>>T;
	while(T--){
		int n;
		cin>>n;
		string s;
		cin>>s;
		int cnt1 = 0,t = 0,cnt = 0;
		for(int i=0;i<s.size();i++){
			cnt1 += s[i] == '1';
			t += s[i] == '1'; 
			if(i && s[i] == '0' && s[i - 1] == '1'){
				cnt1 -= t & 1;
				cnt += t & 1;
				t = 0;
			}
		}
		cnt1 -= t & 1;
		cnt += t & 1;
		cout<<C(n - cnt1 - cnt + cnt1 / 2,cnt1 / 2)<<'\n';
	}
	return 0;
}
posted @ 2026-08-07 00:13  yutar  阅读(6)  评论(0)    收藏  举报