CF756D 【Bacterial Melee】[dp,组合数]

首先定义一个状态 \(dp_{j,c}\) 表示选了 \(j\) 个位置最后一个字符是 \(c\)
转移方程是

\(dp_{j,c} = \sum dp_{j-1,k} [k \neq c]\)
代表的是长度为 \(j\) 的以 \(c\) 结尾的方案数…

然后你发现,这个其实可以把 abc 变成 aab 的,所以很显然…是跟断点数量有关,假设你有 \(m\) 个断点,你发现你选的这个其实是个组合数 \((^n_m)\) 其实证明就是当做断点来看,然后每次你枚举长度,倒序枚举,大概是01背包的那种想法,把不相同的全部加进来…这样就做完了。

// by Isaunoya
#include<bits/stdc++.h>
using namespace std;
struct io {
	char buf[1 << 23 | 3], *s; int f;
	io() { f = 0, buf[fread(s = buf, 1, 1 << 23, stdin)] = '\n'; }
	io& operator >> (int&x) {
		for(x = f = 0; !isdigit(*s); ++s) f |= *s  == '-';
		while(isdigit(*s)) x = x * 10 + (*s++ ^ 48);
		return x = f ? -x : x, *this;
	}
};

const int maxn = 5e3 + 35;
const int mod = 1e9 + 7;

int C[maxn][maxn];
int dp[maxn][27];

int add(int x, int y) {
	return (x + y >= mod) ? (x + y - mod) : (x + y);
}
signed main() {
	string s;
	vector<int> a;
	ios :: sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
	int len; cin >> len; cin >> s;
	a.clear(); a.push_back(0);
	for(char x: s) {
		int c = x - 'a' + 1;
		if(c == a.back()) continue;
		a.push_back(c);
	}
	int n = len;
	C[0][0] = 1;
	for(int i = 1 ; i <= n ; i ++) {
		for(int j = 1 ; j <= i ; j ++) { C[i][j] = add(C[i - 1][j], C[i - 1][j - 1]); }
	}
	dp[0][0] = 1;
	for(int i = 1 ; i < a.size() ; i ++) {
		for(int j = i ; j ; j --) {
			int x = a[i], ans = 0;
			for(int c = 0; c <= 26; c ++) { if(c ^ x) { ans = add(ans, dp[j - 1][c]); } }
			dp[j][x] = ans;
		}
	}
	int ans = 0;
	for(int i = 1 ; i < a.size(); i ++) {
		int tmp = 0;
		for(int c = 1 ; c <= 26 ; c ++) tmp = add(tmp, dp[i][c]);
		ans = add(ans, 1ll * C[n][i] * tmp % mod);
	}
	cout << ans << '\n'; 
	return 0;
}
posted @ 2020-05-02 18:18  _Isaunoya  阅读(194)  评论(0编辑  收藏  举报