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;
}
posted @ 2022-04-14 20:33  qingyanng  阅读(54)  评论(0编辑  收藏  举报