CF1622D (组合数学) (2000)
题意: 给出一个长度为n的01串,可以做一次操作:选择恰好含k个1的字串,给这个字串重新排序。问可以得到多少种串。
- 对于一个字串的排序方案是多重集排列,len!/(zero!* one!) 或者说组合数C(len,one)
- 显然,对于选择一次恰好含k个1的字串,要选择极长的比如01100的方案包含了0110。
- 两个区间有交集,减去交集的部分
- 因为每次找到一个新的区间都是去掉左端的1,再在右端添上1。 我们把1的位置存到vector里面进行枚举。极长的区间就是当前区间右端1的后面一个1的前一个位置,左端1的前面一个1的后一个位置。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
typedef __int128_t li;
//#define int long long
#define PII pair<int, int>
const int N = 1e4 + 5;
const int M = 1e2 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double PI = acos(-1.0);
ll F[N], inv[N];
ll qmi(ll m, ll k ) {
ll res = 1;
while(k) {
if(k & 1) res = res * m % mod;
m = m * m % mod;
k >>= 1;
}
return res;
}
void init() {
int n = N - 1;
F[0] = 1, 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;
}
int main () {
init();
int n, k; cin >> n>> k;
string ss; cin >> ss;
ss = "*" + ss; vector<int> ve;
ve.push_back(0);
for ( int i = 1; i <= n; ++ i ) if(ss[i] == '1') ve.push_back(i);
ve.push_back(n + 1);
ll res = 0;
for ( int i = k; i < ve.size() - 1; ++ i ) {
res = (res + F[ve[i + 1] - 1 - ve[i - k]] * inv[k] % mod * inv[ve[i + 1] - 1 - ve[i - k] - k] % mod ) % mod;
if(i > k) res = (res - F[ve[i] - 1 - ve[i - k]] * inv[k - 1] % mod * inv[ve[i] - 1 - ve[i - k] - k + 1] % mod + mod) % mod;
}
if (k == 0||ve.size()-2<k)cout << 1 << endl;
else cout << res << endl;
return 0;
}