题目链接:~~~
题目大意:
在一个 1 * n 的棋盘中,0 表示没旗子, 1 表示有棋子。
有两个操作:
- 把一个棋子(假设在位置 i) 放到 i + 2,如果满足 i+2 <=n 并且 i+1 有棋子和 i + 2 没棋子
- 把一个棋子(假设在位置 i) 放到 i - 2,如果满足 i - 2 <=n 并且 i-1 有棋子和 i - 2 没棋子
给出 n 和一个棋盘的初始状态,可以通过这两种操作得到多少种棋局,结果对 998244353 取模
分析:
第一点:这里的 0 和 1 是无区别的(000,三个 0 没有区别
第二点:明显需要 11 才可以进行移动,而单个的 1 不能进行操作,并且单个的 1 对结果无影响
eg:01011 等价于 0011
01011 对应的情况为:01110、11010
0011 对应的情况为: 0110、 1100
也就是说,当 11 移动到一个单个 1 时,11可以抛弃原来的一个 1,和单个的一个 1 捆绑,最后的结果就是,可以忽略单个的 1
第三点:最后的结果就是,0 和 11 进行排列组合,统计 0 和 11 的个数,求一下组合数就好
参考代码:
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod=998244353;
const int maxn = 1e5 + 5;
using namespace std;
// 快速幂求逆元
ll pow_mod(ll a, ll b, ll p){ // a 的 b 次方求余 p
ll ans = 1;
while(b){
if(b & 1) ans = (ans * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ans;
}
//分数取余 (a/b) % p = a * (b^-1) % p = a * (b ^ p-2) % p
ll inv(ll a, ll b, ll p){
return (a * pow_mod(b, p-2, p)) % p;
}
//费马求 a 关于 p 的逆元
ll inv(ll a, ll p){
return pow_mod(a, p-2, p);
}
ll fac[200005]; // 阶乘
//计算组合数 Cmn
ll C(ll m, ll n, ll p){
return fac[n] * inv(fac[m], p) % p * inv(fac[n-m], p) % p;
}
int main(){
fac[0] = 1;
for(int i = 1; i < maxn; i++) fac[i] = fac[i-1] * i % mod;
int ncase;
cin >> ncase;
while(ncase--){
int n;
cin >> n;
string s;
cin >> s;
int tot = 0, num = 0, len = 0; char last = '.';
for(auto i : s){
if(i == '0'){
tot++;
if(i != last){
tot += len / 2;
num += len / 2;
len = 0;
}
}
else{
if(last == '1') len++;
else len = 1;
}
last = i;
}
tot += len / 2;
num += len / 2;
cout << C(num, tot, mod) << endl;
}
return 0;
}