翻硬币
jzoj 3921
题目大意:
给你一个长度为 n n n的当前01串和目标01串,现在你要做 m m m此操作,每次操作你要使 k k k个不同的位取反,现在问你有多少种方法可以使当前01串变为目标01串
输入样例:
3 2 1
100
001
输出样例:
2
样例解释:
100
−
>
101
−
>
001
100->101->001
100−>101−>001
100
−
>
000
−
>
001
100->000->001
100−>000−>001
数据范围
对于30% 的数据,
N
<
=
4
;
,
0
<
=
K
<
=
5
N <=4;,0 <= K <= 5
N<=4;,0<=K<=5
对于60% 的数据,
N
<
=
10
N <= 10
N<=10
对于100% 的数据,
1
<
=
N
<
=
100
;
,
0
<
=
K
<
=
100
,
0
<
=
M
<
=
N
1 <= N <= 100;,0 <= K <= 100,0 <= M <= N
1<=N<=100;,0<=K<=100,0<=M<=N
解题思路:
我们设
f
i
,
j
f_{i,j}
fi,j为第
i
i
i次操作,与目标操作有
j
j
j位不同的种数
当
j
⩾
k
j\geqslant k
j⩾k时(
j
<
k
j<k
j<k的情况见代码)
我们从
f
i
−
1
,
j
f_{i-1,j}
fi−1,j转移到
f
i
,
k
f_{i,k}
fi,k首先一定要取反
j
−
k
j-k
j−k次
多余的
m
−
(
j
−
k
)
m-(j-k)
m−(j−k)平分成与结果相同的和不同的来取反
这样我们就得出了状态转移方程:
f
t
,
j
=
f
t
,
j
+
f
t
−
1
,
i
∗
C
i
m
−
(
j
−
k
)
2
∗
C
n
−
i
j
−
i
+
(
m
−
(
j
−
k
)
2
)
(
j
⩾
k
)
f_{t,j}=f_{t,j}+f_{t-1,i} * C_i^{\frac{m - (j - k)}{2}}* C_{n - i}^{j - i + (\frac{m - (j - k)}{2})}\ \ \ \ \ \ (j \geqslant k)
ft,j=ft,j+ft−1,i∗Ci2m−(j−k)∗Cn−ij−i+(2m−(j−k)) (j⩾k)
注:代码中的变量有所不同
代码:
5
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define abs(x) (x) < 0? -(x) : (x)
#define ll long long
#define wyc 1000000007//%%%
using namespace st
d;
ll n, m, k, s, C[150][150], f[150][150];
string str, str1;
int main()
{
for (int i = 0; i <= 100; ++i)
C[i][0] = 1;
for (int i = 1; i <= 100; ++i)
for (int j = 1; j <= 100; ++j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % wyc;//求组合数
scanf("%lld%lld%lld", &n, &k, &m);
cin>>str;
cin>>str1;
for (int i = 0; i < n; ++i)
s += (str[i] != str1[i]);
if (s&1 && !(m&1))//偶数无法拼成基数
{
printf("0");
return 0;
}
f[0][s] = 1;
for (ll t = 1; t <= k; ++t)
for (ll i = 0; i <= n; ++i)
for (ll j = max((i + m) & 1, i - m); j <= min(n, i + m); j += 2)//一些小优化
if ((i + j + 1)&1 && abs(i - j) <= m)//也是一些小优化
{
if (j >= i)
f[t][j] = (f[t][j] + f[t - 1][i] * C[i][((m - (j - i)) >> 1)] % wyc * C[n - i][j - i + ((m - (j - i)) >> 1)] % wyc) % wyc;//状态转移
else f[t][j] = (f[t][j] + f[t - 1][i] * C[i][i - j + ((m - (i - j)) >> 1)] % wyc * C[n - i][((m - (i - j)) >> 1)] % wyc) % wyc;//反过来
}
printf("%lld", f[k][0]);
return 0;
}