牛客练习赛98D Son String (组合数学 计数DP)
https://ac.nowcoder.com/acm/contest/11188/D
- 划分问题有时候适用隔板法
- 要求对称的1相等,我们做dp来划分状态
- 全0的时候直接算每个位置放不放隔板,放边上等价全不放, 偶数个奇数时候,还要计算中间的0的全零的方案 101
#include<bits/stdc++.h>
//#include <bits/extc++.h>
using namespace std;
// using namespace __gnu_cxx;
// using namespace __gnu_pbds;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define li __int128_t
//#define int long long
const int N = 5e2 + 5;
const int M = 1e6 + 5;
const int mod = 998244353;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
int a[N], n;
ll f[N];
ll qmi( ll m, ll k ) {
ll res = 1 % mod;
while( k ) {
if(k & 1) res = res * m % mod;
m = m * m % mod;
k >>= 1;
}
return res;
}
ll F[N];ll inv[N];
void init(int n){
F[0]= inv[0] = 1;
for(int i=1;i<=n;i++)F[i]=F[i-1]*i%mod;
inv[n] = qmi(F[n], mod - 2);
for(int i=n-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
ll C ( ll a, ll b ) {
return F[a] * inv[b] % mod * inv[a - b] % mod;
}
int main() {
init(505);
string ss; cin >> ss;
for ( int i = 0; i < ss.length(); ++ i ) {
if( (ss[i] - '0') & 1 ) a[ ++ n ] = i + 1;
}
int len = ss.length();
if(!n) {
cout << qmi( 2, len - 1 ) << '\n'; return 0;
}
for ( int i = 1; i <= (n + 1) / 2 ; ++ i ) {
if( i == 1 ) {
int f1 = a[i] - 1, f2 = len - a[n];
for ( int j = 0; j <= min(f1, f2); ++ j) {
f[1] = (f[1] + C(f1, j) * C(f2, j) % mod) % mod;
}
} else {
int f1 = a[i] - a[i - 1] - 1 + 1, f2 = a[n - i + 2] - a[n - i + 1] - 1 + 1;
for ( int j = 0; j <= min(f1, f2); ++ j) {
f[i] = (f[i] + C(f1, j) * C(f2, j) % mod * f[i - 1] % mod) % mod;
}
}
}
if( n % 2 == 0 ) {
int num = a[n/2 + 1] - a[n / 2] - 1;
cout << f[ n / 2 ] * qmi( 2, num + 1) % mod << '\n';
} else {
cout << f[ n / 2 + 1] % mod << '\n';
}
return 0;
}