题目大意
有n种巧克力,Facer一开始拥有一些巧克力,每种最多一个,他的女友一开始也有一些巧克力,每种也是最多一个。
现有一个大箱子里面装了n种巧克力(可以认为有无穷多),Facer可以执行m次操作,每次Facer可以从中每次选取3种巧克力各一个,如果他一旦拥有的巧克力中有2个是同一个种类,那么他就会把这2个都吃掉。并且每次选取的巧克力种类组合不能相同。
执行完m次操作后,要求facer拥有的巧克力和他的女朋友完全相同。
问:Facer进行操作的方案数(算选集合相同顺序不同的算一种)。
样例
4 3
1101
1001
即有4种巧克力,执行3次操作
facer一开始拥有的巧克力集合为 1101
女朋友为 1001
算法分析
最开始facer拥有的巧克力集合为s,他的女朋友拥有的巧克力集合为t。
那么最终的结果需要s变为t,那么S=s xor t 就是需要变换的位的情况。
如样例所示 S=0100。
可以看作从0变为S (0100)经过m次操作有多少种方案。
设S表示的二进制中含有T个1,即有T种巧克力。
那么这T个1其实在哪些位上是没有区别的。
因为 从0000 -> 0100和从0000 -> 0001 经过m次操作并不会影响方案的个数。
从实际上来看,一开始没有巧克力,进过m次操作,留下第a种巧克力,和留下第b种巧克力的方案数是同样的。
所以我们可以设状态转移方程F(m)(T) 代表经过m次不重复操作,有T个1的方案数。
F[m][T]=(∑i=03F[m−1][T+2∗i−3]∗(n−Ti)∗(T3−i) - F[n−2][T]*((n3)−m+2))/m
(n−Ti)代表组合数C(n-T,i);
推导
-
在更新F(m)(T)时,先枚举下一步的操作的集合:
从已有的T种种选了3种,那么现有的T=T−3。
从已有的T种中选了2种,吃掉2个T=T−2,再从没有的种数n−T中选了一种 T=T+1 => T=T−1
从已有的T种种选了1种,T=T−1,再从没有的种数n−T中选了2种T=T+2 => T=T+1
从已有的T种种选了0种,T=T+3;
F[m−1][T+2∗i−3]∗(n−Ti)∗(T3−i)
i代表新加入的1的个数i=0,1,2,3。 -
在我们每次选取新的新的操作的时候,按照上述公式,即我们在执行第m次操作,可能会选出与前m−1次操作相同的组合。那么就有了重复。总共有m−2个不重复的操作,那么选取2种组合重复的种类个数为(n3)−(m−2),那么总共的重复次数为:F[m−2][T]∗((n3)−m+2)
-
这一次的操作可以出现在m次中的任意一次 所以要除以m. 即在枚举得时候 比如 我们已经得到操作了2次的集合,在第三次操作枚举第三个集合时,我们对集合(A,B) 枚举出C,对集合(A,C)枚举出B,对集合(B,C)枚举出A,这样 我们的集合A,B,C重复了3 次。
-
在得到结果上述递推式后,进行记忆化搜索。
-
在搜索过程中会遇到对1/m 求mod 这时候要用到乘法逆元 http://www.cnblogs.com/dupengcheng/p/5487362.html
-
预处理每个组合数的结果和乘法逆元。不然会TLE
-
时间复杂度最差大约在O(10^6)
-
//Author: Wenjun Shi //on 2016/7/18 #include <iostream> #include <cstdio> #include <cstring> #define MOD 10007 #define MAX_N 1024 #define MAX_M 1024 #define MAX_T 1024 using namespace std; typedef long long ll; int dp[MAX_M][MAX_T]; int n,m; int BF,GF,S; int c[MAX_N][MAX_M]; int inv[MAX_N]; int mod_pow(int x,int n) { if(n==0) return 1%MOD; else { ll temp=mod_pow(x,n>>1); temp=temp*temp%MOD; if(n&1) temp=temp*x%MOD; return temp; } } int niv(int m) { return mod_pow(m,MOD-2); } void init() { for (int i = 1; i < MAX_N; ++i) { inv[i] = niv(i); } for(int i=0;i<MAX_N;i++) { c[i][0]=c[i][i]=1; for(int j=1;j<i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD; } } int dfs(int m,int T) { if(dp[m][T]!=-1) return dp[m][T]; if(m==1) return dp[1][T]=(T==3?1:0); ll ans=0; for(int i=0;i<4;i++) { if (n - T < i || T < 3 - i) continue; ans+=((ll)dfs(m-1,T+2*i-3)*c[n-T][i]*c[T][3-i])%MOD; } ans-=((ll)dfs(m-2,T)*(c[n][3]-m+2))%MOD; ans=ans*inv[m]%MOD; if(ans<0) ans+=MOD; return dp[m][T]=ans; } int main() { char s1[MAX_N],s2[MAX_N]; init(); dp[0][0]=1; while(~scanf("%d%d",&n,&m)&&n!=0){ scanf("%s%s",s1,s2); int T=0; for(int i=0;i<n;i++) { if(s1[i]!=s2[i]) T++; } memset(dp,-1,sizeof(dp)); dp[0][0]=1; printf("%d\n",dfs(m,T)); } return 0; }