zoukankan      html  css  js  c++  java
  • 【poj3718】 Facer's Chocolate Dream

    http://poj.org/problem?id=3718 (题目链接)

    题意

      给出${2}$个长度为${n}$的${01}$串,问是否存在${m}$个长度为${n}$的有三个位置为${1}$的$01$串它们的异或和正好等于那两个$01$串的异或和。求方案数。

    Solution

      我们把那两个$01$串异或起来得到一个$S$,那么问题就变成了是否存在$m$个长度为$n$的有且仅有三个位置为$1$的$01$串的异或和等于$S$。

      我们这样进行考虑,假设$S$有$v$个位置上为$1$,先求出异或出来的串有$v$个位置为$1$的方案数,因为$v$个$1$可以任意组合,我们再除以一个组合数$C_{n}^{v}$就是答案。

      再添加一个条件,我们假设选出的$m$个数不同顺序算是不同的方案,求出来以后再除以一个$m!$就可以了。

      这样我们就可以dp啦。$f[i][j]$表示已经选了$i$个数,异或起来有$j$个$1$的方案数,那么最后答案就是$$ans=frac{f[m][v]}{m!C_n^v}$$

      考虑转移,有这几种情况:

    egin{aligned}  f[i][j]&=f[i-1][j-3]inom{n-j+3}{3}  \  &+f[i-1][j-1]inom{n-j+1}{2}(j-1)  \  &+f[i-1][j+1](n-j-1)inom{j+1}{2}  \  &+f[i-1][j+3]inom{j+3}{3}   end{aligned}

      这应该很好理解吧,就是新加入的第$i$个数,它会对之前$i-1$个数已经异或出来的数造成的影响有$4$种:将三个$0$变成$1$;将两个$0$变成$1$,一个$1$变成$0$;将一个$0$变成$1$,两个$1$变成$0$;将三个$1$变成$0$。

      考虑到这样转移,并没有考虑第$i$个数与之前的$i-1$个数重复的情况。如果第$i$个数与之前某个数重复了,那么就意味着剩下的$i-2$个数异或起来还是会有$j$个$1$。所以我们还要容斥一下:

    egin{aligned}  f[i][j]-=f[i-2][j]inom{n}{3}(i-1)   end{aligned}

      表示第$i$个数的取值有$inom{n}{3}$种,它可以与之前$i-1$个数中的任意一个相等。

      那么就做完了,最后转移的时候注意判一下边界。

    细节

      LL

    代码

    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define inf (1ll<<30)
    #define MOD 10007
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=1010;
    int C[maxn][maxn],f[maxn][maxn],fac[maxn];
    int n,m,v;
    char s1[maxn],s2[maxn];
    
    int power(int a,int b) {
    	int res=1;
    	while (b) {
    		if (b&1) res=(LL)res*a%MOD;
    		b>>=1;a=(LL)a*a%MOD;
    	}
    	return res;
    }
    int main() {
    	for (int i=0;i<=1000;i++) C[i][0]=1;
    	for (int i=1;i<=1000;i++)
    		for (int j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
    	fac[0]=1;for (int i=1;i<=1000;i++) fac[i]=(LL)fac[i-1]*i%MOD;
    	for (int i=1;i<=1000;i++) fac[i]=power(fac[i],MOD-2);
    	while (scanf("%d%d",&n,&m)!=EOF && n && m) {
    		scanf("%s%s",s1+1,s2+1);v=0;
    		for (int i=1;i<=n;i++) v+=(s1[i]!=s2[i]);
    		memset(f,0,sizeof(f));f[0][0]=1;
    		for (int i=1;i<=m;i++)
    			for (int j=0;j<=n;j++) {
    				if (j>=3) (f[i][j]+=(LL)C[n-j+3][3]*f[i-1][j-3]%MOD)%=MOD;
    				if (j>=1) (f[i][j]+=(LL)C[n-j+1][2]*(j-1)%MOD*f[i-1][j-1]%MOD)%=MOD;
    				if (j+1<=n) (f[i][j]+=(LL)C[j+1][2]*(n-j-1)%MOD*f[i-1][j+1]%MOD)%=MOD;
    				if (j+3<=n) (f[i][j]+=(LL)C[j+3][3]*f[i-1][j+3]%MOD)%=MOD;
    				if (i>=2) (f[i][j]+=MOD-(LL)f[i-2][j]*(C[n][3]-i+2)%MOD*(i-1)%MOD)%=MOD;
    			}
    		printf("%lld
    ",(LL)f[m][v]*fac[m]%MOD*power(C[n][v],MOD-2)%MOD);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Zend框架2入门(二) (转)
    Zend框架2入门(一) (转)
    PHP Strict standards:Declaration of … should be compatible with that of…(转)
    ::符号
    mysql查询今天,昨天,近7天,近30天,本月,上一月数据的方法(转)
    php 获取今日、昨日、上周、本月的起始时间戳和结束时间戳的方法(转)
    PHP5.4新特性(转)
    PHP5.4的变化关注---What has changed in PHP 5.4.x(转)
    关于PHP的curl开启问题 (转)
    安装apache重启的时候,报错端口被占用,错误1
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6406615.html
Copyright © 2011-2022 走看看