分析:
很典型的组合数学+DP的题。
定义
DP[i][j]
D
P
[
i
]
[
j
]
表示用i个数字,xor出来有j个1,且这i个数字互不相同的方案数。
转移很简单:
DP[i+1][j+3]+=DP[i][j]∗C(n−j,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(n−j,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][j−1]+=DP[i][j]∗(n−j)∗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][j−3]+=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);
}
}