HDU 6391 Lord Li's problem(dp+组合数学)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V5ZSQ/article/details/82562856

Description

对于 0 ~ 2 n 1 这些数字中二进制表示只有三个 1 的数字集,问从中选取一个 k 子集与 S 异或结果为 T 的方案数

Input

多组用例,每组用例首先输入两个整数 n , k ,之后输入两个长度为 n 01 串为 S , T 的二进制表示,以 0   0 结束输入

( 1 n 40 , 0 k m i n ( 20 , C n 3 ) )

Output

输出方案数,结果模 19260817

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

问题转化为用不同的 k 个二进制表示只有三个 1 n 位二进制数异或得到 S ^ T ,假设 S ^ T m 个位置为 1 ,显然方案数只和 m 有关,与这 m 1 的具体位置无关,那么我们只需使得这 k 个数字异或结果的后 m 位为 1 ,前 n m 位为 0 即可,以 d p [ i ] [ j ] 表示用 i 个不同的数字异或结果为后 j 位为 1 ,前 n j 位为 0 的方案数,考虑第 i 个数的贡献,有四种情况

1 . 3 1 全部在之前为 0 的位,也即从当前的 j 1 中找 3 个位置放置当前数字的 3 1 ,故有转移

d p [ i ] [ j ] + = C j 3 d p [ i 1 ] [ j 3 ]

2 . 2 1 在之前为 0 的位, 1 1 在之前为 1 的位,也即从当前的 j 1 中找 2 个位置放置当前数字产生的 2 1 ,从当前的 n j 1 里找 1 个位置放置当前数字消除的 1 1 ,故有转移
d p [ i ] [ j ] + = C j 2 C n j 1 d p [ i 1 ] [ j 1 ]

3 . 1 1 在之前为 0 的位, 2 1 在之前为 1 的位,也即从当前的 j 1 中找 1 个位置放置当前数字产生的 1 1 ,从当前的 n j 1 里找 2 个位置放置当前数字消除的 2 1 ,故有转移
d p [ i ] [ j ] + = C j 1 C n j 2 d p [ i 1 ] [ j + 1 ]

4 . 3 1 全部在之前的 1 位,也即从当前的 n j 0 3 个位置放置当前数字消除的 3 1 ,故有转移
d p [ i ] [ j ] + = C n j 3 d p [ i 1 ] [ j + 3 ]

首先注意到此时我们放置的第 i 个数字可能会与前面的 i 1 个数字中的某个重复,故有
d p [ i ] [ j ] = ( C n 3 ( i 2 ) ) d p [ i 2 ] [ j ]

表示前 i 2 个不同的数字已经使得后 j 位为 1 ,前 n j 位为 0 ,而第 i 个数字重复出现了两次,选取一个和前 i 2 个数字不同的数字均可

其次我们不应考虑数字的前后顺序,故有

d p [ i ] [ j ] = d p [ i ] [ j ] i

单组用例时间复杂度 O ( n k ) ,总时间复杂度 O ( T n k ) ,注意到可以预处理每个长度的答案,对于每组用例只需统计 m 即可,时间复杂度 O ( n 2 k + T n )

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;
}

猜你喜欢

转载自blog.csdn.net/V5ZSQ/article/details/82562856