*题解: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;
}

浙公网安备 33010602011771号