翻硬币
jzoj 3921
题目大意:
给你一个长度为nnn的当前01串和目标01串,现在你要做mmm此操作,每次操作你要使kkk个不同的位取反,现在问你有多少种方法可以使当前01串变为目标01串
输入样例:
3 2 1
100
001
输出样例:
2
样例解释:
100−>101−>001100->101->001100−>101−>001
100−>000−>001100->000->001100−>000−>001
数据范围
对于30% 的数据,N<=4;,0<=K<=5N <=4;,0 <= K <= 5N<=4;,0<=K<=5
对于60% 的数据,N<=10N <= 10N<=10
对于100% 的数据,1<=N<=100;,0<=K<=100,0<=M<=N1 <= N <= 100;,0 <= K <= 100,0 <= M <= N1<=N<=100;,0<=K<=100,0<=M<=N
解题思路:
我们设fi,jf_{i,j}fi,j为第iii次操作,与目标操作有jjj位不同的种数
当j⩾kj\geqslant kj⩾k时(j<kj<kj<k的情况见代码)
我们从fi−1,jf_{i-1,j}fi−1,j转移到fi,kf_{i,k}fi,k首先一定要取反j−kj-kj−k次
多余的m−(j−k)m-(j-k)m−(j−k)平分成与结果相同的和不同的来取反
这样我们就得出了状态转移方程:
ft,j=ft,j+ft−1,i∗Cim−(j−k)2∗Cn−ij−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;
}