L i n k Link Link
J Z O J JZOJ JZOJ 3921 3921 3921
D e s c r i p t i o n Description Description
小X 离开家的时候忘记带走了钱包,掉下的硬币在桌子上排成了一列。正在等着哥哥回来的小Y坐在桌子旁边,无聊地翻着桌子上的硬币。
出于某种爱好,小Y 一次一定会同时翻转M 枚硬币。由于小Y 是一个爱动脑的小学生,这样进行了若干次之后她很快想到了一个问题:有多少种方法能够在K 次翻转后把硬币由原来的状态变成现在这样呢?
因为小Y 是个好学的小学生,她只需要你告诉她方案数对1000000007 取模的值以方便她进行验算就可以了。
I n p u t Input Input
第一行,包含三个字符N;K;M,表示硬币的数量,翻转的次数和每次翻转的硬币数量。
第2 3 行,包含N 个字母,表示硬币在一开始的状态和最终要变成的状态。1 表示正面而0 表示背面。
O u t p u t Output Output
一行包含一个整数,表示方案数对1000000007 取模的值。
S a m p l e Sample Sample I n p u t Input Input
3 2 1
100
001
S a m p l e Sample Sample O u t p u t Output Output
2
H i n t Hint Hint
样例解释:
100->101->001
100->000->001
• 对于30% 的数据,N <=4; 0 <= K <= 5。
• 对于60% 的数据,N <= 10。
• 对于100% 的数据,1 <= N <= 100; 0 <= K <= 100; 0 <= M <= N。
T r a i n Train Train o f of of T h o u g h t Thought Thought
DP,动态转移方程:
f
[
i
]
[
f
d
]
=
f
[
i
]
[
f
d
]
+
f
[
i
−
1
]
[
j
]
∗
C
[
j
]
[
k
k
]
∗
C
[
n
−
j
]
[
m
−
k
k
]
f[i][fd] = f[i][fd] + f[i - 1][j] * C[j][kk] * C[n - j][m - kk]
f[i][fd]=f[i][fd]+f[i−1][j]∗C[j][kk]∗C[n−j][m−kk]
C o d e Code Code
#include<iostream>
#include<cstdio>
const int Mod = 1000000007;
using namespace std;
int n, k, m, dif, fd;
long long C[101][101], f[101][101];
int a[5][101];
int main()
{
C[0][0] = 1;
scanf("%d%d%d", &n, &k, &m);
for (int i = 1; i <= n; ++i) {//求组合数
C[i][0] = 1;
for (int j = 1; j <= i; ++j)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % Mod;
}
for (int kk = 1; kk <= 2; ++kk)
for (int i = 1; i <= n; ++i)
scanf("%1d", &a[kk][i]);
for (int i = 1; i <= n; ++i)
if (a[1][i] != a[2][i]) ++dif; //算出有多少个不同
f[0][dif] = 1;
for (int i = 1; i <= k; ++i)
for (int j = 0; j <= n; ++j)
for (int kk = 0; kk <= min(m, j); ++kk)//最多看能翻多少个
{
if (n - j >= m - kk)
{
fd = j - kk + m - kk;//翻了之后有多少不同
f[i][fd] = (f[i][fd] + f[i - 1][j] * (C[j][kk] * C[n - j][m - kk] % Mod) % Mod) % Mod;
}
}
printf("%lld", f[k][0]);
}