POJ-3717-Facer's Chocolate Dream

题目大意

nn种巧克力,Facer一开始拥有一些巧克力,每种最多一个,他的女友一开始也有一些巧克力,每种也是最多一个。

现有一个大箱子里面装了nn种巧克力(可以认为有无穷多),Facer可以执行mm次操作,每次Facer可以从中每次选取33种巧克力各一个,如果他一旦拥有的巧克力中有22个是同一个种类,那么他就会把这22个都吃掉。并且每次选取的巧克力种类组合不能相同。

执行完mm次操作后,要求facer拥有的巧克力和他的女朋友完全相同。
问:Facer进行操作的方案数(算选集合相同顺序不同的算一种)。

样例
4 3
1101
1001

即有44种巧克力,执行33次操作
facer一开始拥有的巧克力集合为 11011101
女朋友为 10011001

算法分析

最开始facer拥有的巧克力集合为ss,他的女朋友拥有的巧克力集合为tt
那么最终的结果需要ss变为tt,那么S= s~xor~tS=s xor t  就是需要变换的位的情况。
如样例所示 S=0100S=0100

可以看作从00变为S~(0100)S (0100)经过mm次操作有多少种方案。
SS表示的二进制中含有TT11,即有TT种巧克力。
那么这TT11其实在哪些位上是没有区别的。

因为 从00000000 -> 01000100和从00000000 -> 00010001 经过mm次操作并不会影响方案的个数。
从实际上来看,一开始没有巧克力,进过mm次操作,留下第aa种巧克力,和留下第bb种巧克力的方案数是同样的。

所以我们可以设状态转移方程F(m)(T)F(m)(T) 代表经过mm次不重复操作,有TT11的方案数。

F[m][T]=\big(\sum_{i=0}^{3}{F[m-1][T+2*i-3] *{{i}\choose{n-T}} *{{3-i}\choose{T}}}F[m][T]=(i=03F[m1][T+2i3](nTi)(T3i) - F[n-2][T]F[n2][T]*({{3}\choose{n}}-m+2)\big)/m(n3)m+2))/m

(nTi)代表组合数C(n-T,i);

推导

  1. 在更新F(m)(T)F(m)(T)时,先枚举下一步的操作的集合:
    从已有的T种种选了33种,那么现有的T=T-3T=T3
    从已有的T种中选了22种,吃掉22T=T-2T=T2,再从没有的种数n-TnT中选了一种 T=T+1T=T+1 => T=T-1T=T1
    从已有的TT种种选了11种,T=T-1T=T1,再从没有的种数n-TnT中选了22T =T+2T=T+2 => T=T+1T=T+1
    从已有的T种种选了00种,T=T+3T=T+3;
    {F[m-1][T+2*i-3] *{{i}\choose{n-T}} *{{3-i}\choose{T}}}F[m1][T+2i3](nTi)(T3i)
    i代表新加入的11的个数i=0,1,2,3i=0,1,2,3

  2. 在我们每次选取新的新的操作的时候,按照上述公式,即我们在执行第mm次操作,可能会选出与前m-1m1次操作相同的组合。那么就有了重复。总共有m-2m2个不重复的操作,那么选取2种组合重复的种类个数为{{{3}\choose{n}} -(m-2)}(n3)(m2),那么总共的重复次数为:{F[m-2][T] *({{3}\choose{n}} -m+2)}F[m2][T]((n3)m+2)

  3. 这一次的操作可以出现在mm次中的任意一次 所以要除以mm. 即在枚举得时候 比如 我们已经得到操作了2次的集合,在第三次操作枚举第三个集合时,我们对集合(A,B) 枚举出C,对集合(A,C)枚举出B,对集合(B,C)枚举出A,这样 我们的集合A,B,C重复了3 次。

  4. 在得到结果上述递推式后,进行记忆化搜索。

  5. 在搜索过程中会遇到对1/m 求mod 这时候要用到乘法逆元    http://www.cnblogs.com/dupengcheng/p/5487362.html

  6. 预处理每个组合数的结果和乘法逆元。不然会TLE

  7. 时间复杂度最差大约在O(10^6)

  8. //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;
    }
    
    


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值