题目链接:

https://codeforces.com/problemset/problem/1622/D

题目大意:

给定一个只含01的字符串,你可以选择其中一个包含 \(k\) 个 1 的子串,然后对这个子串重新排序,计算可以获得多少个不同的字符串。

思路:

因为符合条件的子串有可能有很多个,如果只是选取包含 \(k\) 个 1 的子串然后重新排列,则需要判断每个子串重新排序后形成的字符串是否重复,这个很难实现。
于是考虑固定子串的起点和终点,这样子每个子串重新排序之后形成的字符串就不会出现重复的情况,我们可以假设子串的起点和终点都是 1,可以通过嵌套循环去选取起点和终点,然后计算能形成的不同子串的数量就可以了。
而计算一个子串重排列后能够形成多少个不同的字符串,需要知道子串含 1 的数量, 0 的数量为 a,1 的数量为 b,那么总共能形成 \(C_{a + b}^b\) 不同的字符串,这样子长度为 \(a + b\),包含 \(b\) 个 1 数量的子串能形成的不同的字符串的数量就可以直接求出来。

代码:

#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int n, k;
string s;
void solve(){
	int ans = 1;
	cin >> n >> k >> s;
	vector <int> p(n + 1, 0);
	for (int i = 1; i <= n; i++)
		p[i] = p[i - 1] + (s[i - 1] - '0');  //求出 1 的数量的前缀和
	if (p[n] < k){
		cout << "1\n";
		return;
	}
	vector <vector <int> > cnt(n + 1);
	for (int i = 0; i <= n; i++){  //计算长为 i,包含 j 个 1 的字符串重排列后能形成不同的字符串
		cnt[i].resize(i + 1);
		cnt[i][0] = cnt[i][i] = 1;
		for (int j = 1; j < i; j++)
			cnt[i][j] = (cnt[i - 1][j] + cnt[i - 1][j - 1]) % mod;
	}
	for (int i = 0; i < n; i++){
		for (int j = i + 1; j < n; j++){
			int a = j - i - 1;
			int b = p[j + 1] - p[i];
			if (b > k) continue;
			b -= ((s[i] - '0') ^ 1) + ((s[j] - '0') ^ 1);  //固定起点和终点为 1
			if(b < 0 || a < 0 || b > a) continue;
			ans = (ans + cnt[a][b]) % mod;
		}
	}
	cout << ans << "\n";
}
int main(){
	solve();
	return 0;
}
posted on 2022-01-15 17:26  Hamine  阅读(253)  评论(0编辑  收藏  举报