Description
对于 ~ 这些数字中二进制表示只有三个 的数字集,问从中选取一个 子集与 异或结果为 的方案数
Input
多组用例,每组用例首先输入两个整数 ,之后输入两个长度为 的 串为 的二进制表示,以 结束输入
Output
输出方案数,结果模
Sample Input
4 3
1101
1001
3 1
101
010
5 3
11010
10111
0 0
Sample Output
Case #1: 1
Case #2: 1
Case #3: 6
Solution
问题转化为用不同的 个二进制表示只有三个 的 位二进制数异或得到 ^ ,假设 ^ 有 个位置为 ,显然方案数只和 有关,与这 个 的具体位置无关,那么我们只需使得这 个数字异或结果的后 位为 ,前 位为 即可,以 表示用 个不同的数字异或结果为后 位为 ,前 位为 的方案数,考虑第 个数的贡献,有四种情况
.
个
全部在之前为
的位,也即从当前的
个
中找
个位置放置当前数字的
个
,故有转移
. 个 在之前为 的位, 个 在之前为 的位,也即从当前的 个 中找 个位置放置当前数字产生的 个 ,从当前的 个 里找 个位置放置当前数字消除的 个 ,故有转移
. 个 在之前为 的位, 个 在之前为 的位,也即从当前的 个 中找 个位置放置当前数字产生的 个 ,从当前的 个 里找 个位置放置当前数字消除的 个 ,故有转移
. 个 全部在之前的 位,也即从当前的 个 找 个位置放置当前数字消除的 个 ,故有转移
首先注意到此时我们放置的第 个数字可能会与前面的 个数字中的某个重复,故有
表示前 个不同的数字已经使得后 位为 ,前 位为 ,而第 个数字重复出现了两次,选取一个和前 个数字不同的数字均可
其次我们不应考虑数字的前后顺序,故有
单组用例时间复杂度 ,总时间复杂度 ,注意到可以预处理每个长度的答案,对于每组用例只需统计 即可,时间复杂度
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define mod 19260817
int mul(int x,int y)
{
ll z=1ll*x*y;
return z-z/mod*mod;
}
int add(int x,int y)
{
x+=y;
if(x>=mod)x-=mod;
return x;
}
int n,k,inv[22],C[44][44],dp[44][22][44];
char a[44],b[44];
void init()
{
inv[1]=1;
for(int i=2;i<=20;i++)inv[i]=mul(mod-mod/i,inv[mod%i]);
for(int i=0;i<=40;i++)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)C[i][j]=add(C[i-1][j-1],C[i-1][j]);
}
for(int n=1;n<=40;n++)
{
dp[n][0][0]=1;
for(int i=1;i<=min(20,C[n][3]);i++)
for(int j=0;j<=n;j++)
{
if(j+1<=n)dp[n][i][j]=add(dp[n][i][j],mul(dp[n][i-1][j+1],mul(C[j][1],C[n-j][2])));
if(j+3<=n)dp[n][i][j]=add(dp[n][i][j],mul(dp[n][i-1][j+3],C[n-j][3]));
if(j>=1)dp[n][i][j]=add(dp[n][i][j],mul(dp[n][i-1][j-1],mul(C[j][2],C[n-j][1])));
if(j>=3)dp[n][i][j]=add(dp[n][i][j],mul(dp[n][i-1][j-3],C[j][3]));
if(i>=2)dp[n][i][j]=add(dp[n][i][j],mod-mul(dp[n][i-2][j],add(C[n][3],mod-(i-2))));
dp[n][i][j]=mul(dp[n][i][j],inv[i]);
}
}
}
int main()
{
int Case=1;
init();
while(~scanf("%d%d",&n,&k),n||k)
{
int m=0;
scanf("%s%s",a,b);
for(int i=0;i<n;i++)m+=(a[i]-'0')^(b[i]-'0');
printf("Case #%d: %d\n",Case++,dp[n][k][m]);
}
return 0;
}