BJ模拟2017 D1T1 计数

计数

题目背景:

BJ模拟2017 D1T1

分析:DP

 

考虑最后的结果,一定是一些由A,B组成的合法段,和一些由C,D组成的合法段交错连接,我们定义f[i]表示,把A,B分成i个合法段的方案数,g[i]表示,把C,D分成i个合法段的方案数,那么显然答案就是:


显然,可以选择,将i - 1段插在i段之间的空隙,或者将i段插在i段之间的空隙和前面,或者i段之间的空隙和后面,或者将i + 1段插在i段之间和前后。显然f[i]g[i]的求得方式是类似的,我们只讨论其中之一就可以了。对于两个字母的合法段只有四种情况,(1)ABAB…A(2)BABA…B(3)ABAB…AB(4)BABA…BA,因为后两种情况中的AB个数相同,那么显然(1) - (2) = n1 - n2,因为AB个数之差固定,所以,我们只需要枚举其中一个的个数,就可以获得另外一个了,对于f[cnt],第(1)种情况出现了i次,第(2)种出现了j = i - n1 + n2次,那么(3), (4)种,共同出现了k = cnt - i - j次,显然(3), (4)是可以等价考虑的,我们直接全部当成(3)情况,然后最后乘上2k就可以了,有了i, j, k我们相当于在i个位置,先放置了一个A,在j个位置先放置了一个Bk个位置放置了一个AB,剩下的AB数量是n1 - i - k个,我们需要把它分配到cnt个位置,这是个经典的组合数问题,方案数是C(n1 - i - k + cnt- 1, cnt - 1),所以:


复杂度O(n2);

 

Source:

 

/*
	created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>

inline char read() {
	static const int IN_LEN = 1024 * 1024;
	static char buf[IN_LEN], *s, *t;
	if (s == t) {
		t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
		if (s == t) return -1;
	}
	return *s++;
}

///*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = read(), iosig = false; !isdigit(c); c = read()) {
		if (c == -1) return ;
		if (c == '-') iosig = true;	
	}
	for (x = 0; isdigit(c); c = read()) 
		x = ((x << 2) + x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}


template<class T>
inline void W(T x) {
	static int buf[30], cnt;
	if (x == 0) write_char('0');
	else {
		if (x < 0) write_char('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) write_char(buf[cnt--]);
	}
}

inline void flush() {
	fwrite(obuf, 1, oh - obuf, stdout);
}

/*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
		if (c == '-') iosig = true;	
	for (x = 0; isdigit(c); c = getchar()) 
		x = ((x << 2) + x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

const int MAXN = 2000 + 10;
const int mod = 1000000000 + 7;

int n1, n2, n3, n4;
int c[MAXN][MAXN], p[MAXN], f[MAXN], g[MAXN];

inline int calc(int x, int y, int cnt) {
	if (cnt == 0) return (x + y == 0);
	if (x < y) std::swap(x, y);
	long long ans = 0;
	for (int i = x - y, end = std::min(x, cnt); i <= end; ++i) {
		int j = i - x + y, k = cnt - i - j;
		if (k < 0) break ;
		ans = (ans + (long long)c[cnt][i] * c[cnt - i][j] % mod * p[k] % mod * 
			c[x - i - k + cnt - 1][cnt - 1]) % mod; 
	}
	return ans;
}

inline void solve() {
	R(n1), R(n2), R(n3), R(n4), p[0] = 1;
	if (n1 + n2 + n3 + n4 == 0) std::cout << "1", exit(0);
	for (int i = 0; i < MAXN; ++i) c[i][0] = c[i][i] = 1;
	for (int i = 1; i < MAXN; ++i) p[i] = (p[i - 1] << 1) % mod;
	for (int i = 2; i < MAXN; ++i)
		for (int j = 1; j < i; ++j)
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
	for (int i = 0; i <= n1 + n2; ++i) f[i + 1] = calc(n1, n2, i);
	for (int i = 0; i <= n3 + n4; ++i) g[i + 1] = calc(n3, n4, i);
	long long ans = 0;
	for (int i = 1; i <= n1 + n2 + 1; ++i) 
		ans += (long long)f[i] * (2LL * g[i] + g[i - 1] + g[i + 1]) % mod;
	std::cout << ans % mod;
}

int main() {
	solve();
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值