[hdu6391]Lord Li's problem

首先发现结果与需要改变的具体位置无关,只和需要改变的位置的个数有关,因此设f[i][j]表示选取了i个数字异或结果有j1,只要分析接下来选择的数和这j1有几个重合即可:

1. 三个数字全部重合,即$f[i][j+3]+=f[i-1][j]\cdot c(n-j,3)$

2. 有两个数字重合,即$[i][j+1]+=f[i-1][j]\cdot c(n-j,2)\cdot j$

3. 有一个数字重合,即$f[i][j-1]+=f[i-1][j]\cdot (n-j)\cdot c(j,2)$

4. 0个数字重合,即$f[i][j-3]+=f[i-1][j]\cdot c(j,3)$

然而这并不正确,因为不能重复选择,因此去掉($c(n,3)-(i-2))\cdot f[i-2][j]$,同时这里我们考虑了操作的顺序和操作的位置,再除以$n!\cdot c(n,m)$才是最终答案。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 19260817
 4 int n,m,t,sum,inv[101],fac[101],f[101][101];
 5 char s1[101],s2[101];
 6 int c(int n,int m){
 7     return 1LL*fac[n]*inv[n-m]%mod*inv[m]%mod;
 8 }
 9 int cc(int n,int m){
10     return 1LL*inv[n]*fac[n-m]%mod*fac[m]%mod;
11 }
12 int main(){
13     inv[0]=inv[1]=fac[0]=fac[1]=1;
14     for(int i=2;i<=40;i++)fac[i]=1LL*fac[i-1]*i%mod;
15     for(int i=2;i<=40;i++)inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
16     for(int i=2;i<=40;i++)inv[i]=1LL*inv[i-1]*inv[i]%mod;
17     while (scanf("%d%d",&n,&m)!=EOF){
18         if ((!n)&&(!m))return 0;
19         scanf("%s%s",s1,s2);
20         sum=0;
21         for(int i=0;i<n;i++)
22             if (s1[i]!=s2[i])sum++;
23         memset(f,0,sizeof(f));
24         f[0][0]=1;
25         for(int i=0;i<=m;i++)
26             for(int j=0;j<=n;j++){
27                 if (i>1)f[i][j]=(f[i][j]-f[i-2][j]*(i-1LL)%mod*(c(n,3)-i+2)%mod+mod)%mod;
28                 for(int k=-1;k<3;k++)
29                     if ((n>j+k)&&(j+k>1))
30                         f[i+1][j+2*k-1]=(f[i+1][j+2*k-1]+1LL*c(n-j,k+1)*c(j,2-k)*f[i][j])%mod;
31             }
32         printf("Case #%d: %d\n",++t,1LL*f[m][sum]*cc(n,sum)%mod*inv[m]%mod);
33     }
34 }
View Code

 

转载于:https://www.cnblogs.com/PYWBKTDA/p/11254646.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值