【DP】【组合数学】HDU6391 Lord Li's problem

27 篇文章 0 订阅

分析:

很典型的组合数学+DP的题。
定义 DP[i][j] D P [ i ] [ j ] 表示用i个数字,xor出来有j个1,且这i个数字互不相同的方案数。

转移很简单:
DP[i+1][j+3]+=DP[i][j]C(nj,3) D P [ i + 1 ] [ j + 3 ] + = D P [ i ] [ j ] ∗ C ( n − j , 3 ) :选3个1,xor上去
DP[i+1][j+1]+=DP[i][j]C(nj,2)j D P [ i + 1 ] [ j + 1 ] + = D P [ i ] [ j ] ∗ C ( n − j , 2 ) ∗ j :选2个1,1个0,xor上去
DP[i+1][j1]+=DP[i][j](nj)C(j,2) D P [ i + 1 ] [ j − 1 ] + = D P [ i ] [ j ] ∗ ( n − j ) ∗ C ( j , 2 ) :选1个1,2个0,xor上去
DP[i+1][j3]+=DP[i][j]                C(j,3) D P [ i + 1 ] [ j − 3 ] + = D P [ i ] [ j ]                                 ∗ C ( j , 3 ) :选3个0,xor上去

然后要注意去重。
去重也非常经典。。。
因为上面的转移式包含了选取所有的值的情况,所以对当前第i个数而言,前(i-1)个中任意一个与它重复的方案都被算上了。

所以就有:
这里写图片描述
然而这并不是最终答案。因为题目中xor的值是指定的,我们定义的 DP[i][j] D P [ i ] [ j ] 中的1是任意的,所以要除去 C(n,j) C ( n , j ) 。而且我们的转移同时还包含了同一方案不同顺序,所以还要除去 k! k !

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 45
#define MAXK 25
#define MOD 19260817
using namespace std;
typedef long long ll;
ll dp[MAXK][MAXN],fac[MAXN],ifac[MAXN];
int n,k;
char s[MAXN],t[MAXN];
ll fsp(ll x,int y){
    ll res=1;
    while(y){
        if(y&1)
            res=res*x%MOD;
        x=x*x%MOD;
        y>>=1;
    }
    return res;
}
void prepare(){
    fac[0]=1;
    for(int i=1;i<=40;i++)
        fac[i]=fac[i-1]*i%MOD;
    ifac[40]=7354954;
    for(int i=40;i>=1;i--)
        ifac[i-1]=ifac[i]*i%MOD;
}
ll C(int x,int y){
    return fac[x]*ifac[y]%MOD*ifac[x-y]%MOD;    
}
int main(){
    prepare();
    int _=0;
    while(SF("%d%d",&n,&k)!=EOF){
        if(n==0&&k==0)
            break;
        memset(dp,0,sizeof dp);
        dp[0][0]=1;
        for(int i=0;i<=k;i++){
            for(int j=0;j<=n;j++){
                if(i>=2)
                    dp[i][j]=(dp[i][j]-dp[i-2][j]*(i-1)%MOD*(C(n,3)-(i-2))%MOD)%MOD;
                dp[i][j]=(dp[i][j]+MOD)%MOD;
                if(j+3<=n)
                    dp[i+1][j+3]=(dp[i+1][j+3]+dp[i][j]*C(n-j,3))%MOD;
                if(j>=1&&j+2<=n)
                    dp[i+1][j+1]=(dp[i+1][j+1]+dp[i][j]*C(n-j,2)%MOD*j)%MOD;
                if(j>=2&&j+1<=n)
                    dp[i+1][j-1]=(dp[i+1][j-1]+dp[i][j]*C(j,2)%MOD*(n-j))%MOD;
                if(j>=3)
                    dp[i+1][j-3]=(dp[i+1][j-3]+dp[i][j]*C(j,3))%MOD;
            }
        }
        SF("%s",s);
        SF("%s",t);
        int cnt=0;
        for(int i=0;i<n;i++)
            if(s[i]!=t[i])
                cnt++;
        PF("Case #%d: %I64d\n",++_,dp[k][cnt]*ifac[k]%MOD*fsp(C(n,cnt),MOD-2)%MOD);
    }
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值