zoukankan      html  css  js  c++  java
  • 51nod 1518 稳定多米诺覆盖(容斥+二项式反演+状压dp)

    [传送门[(http://www.51nod.com/Challenge/Problem.html#!#problemId=1518)

    解题思路

      直接算不好算,考虑容斥,但并不能把行和列一起加进去容斥,这会使时间复杂度非常高,那么就考虑枚举行后(dp)。设(f[i])表示存在(i)列有线,任意一行无线的方案数,(g[i[)表示至少有(i)列有线,任意一行无线的方案数,那么

    [g[i]=sumlimits_{k=i}^n C(i,k)f[i] ]

    二项式反演得

    [f[0]=sumlimits_{k=0}^n(-1)^kg[k]C(k,0) ]

    那么只需要考虑求出(g)
      首先要预处理出来(dp[i][j])表示(i)(j)列任意放的方案数,那么算答案时可以先枚举哪几列有线,然后算出(g[i])(g[i])就是首先把(dp)数组合并,直观理解就是把那几块拼在一起,然后减去(j<i)(g[j]),就是保证行没有线,之后就可以算答案了。时间复杂度O((2^n n^2))

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #define int long long
    
    using namespace std;
    const int N=18;
    const int MOD=1e9+7;
    typedef long long LL;
    
    int f[N][N],n,m,g[N],ans,dp[2][(1<<(17))],tmp[N];
    vector<int> v;
    
    inline void DP(int lim){
    	memset(dp,0,sizeof(dp));
    	int now=0; dp[0][(1<<lim)-1]=1;
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=lim;j++){
    			now^=1; memset(dp[now],0,sizeof(dp[now]));
    			for(int S=0;S<(1<<lim);S++)if(dp[now^1][S]){
    				if(S&1) (dp[now][S>>1]+=dp[now^1][S])%=MOD;
    				if(i!=1 && (!(S&1))) 
    					(dp[now][(S>>1)|(1<<(lim-1))]+=dp[now^1][S])%=MOD;
    				if(j!=1 && (S&1) && lim>1 && (!(S&(1<<(lim-1))))) 
    					(dp[now][(S>>1)|(1<<(lim-1))|(1<<(lim-2))]+=dp[now^1][S])%=MOD; 
    			}
    		}
    		f[i][lim]=dp[now][(1<<lim)-1];
    	}
    }
    
    inline void prework(){
    	for(int i=1;i<=m;i++) DP(i);
    }
    
    signed main(){
    	n=m=16; prework();
    	while(~scanf("%lld%lld",&n,&m)){
    		for(int S=(1<<(m-1));S<(1<<m);S++){
    			int lst=0; v.clear();
    			for(int i=1;i<=m;i++)
    				if(S&(1<<(i-1))) v.push_back(i-lst),lst=i;
    			for(int i=1;i<=n;i++){
    				tmp[i]=1;
    				for(int j=0;j<v.size();j++)
    					tmp[i]=1ll*tmp[i]*f[i][v[j]]%MOD;
    			}	
    			for(int i=1;i<=n;i++){
    				g[i]=tmp[i];
    				for(int k=1;k<i;k++)
    					g[i]-=1ll*g[k]*tmp[i-k]%MOD,g[i]%=MOD;
    			}
    			if(v.size()&1) (ans+=g[n])%=MOD;
    			else (ans-=g[n])%=MOD;
    		} ans=(ans%MOD+MOD)%MOD;
    		printf("%lld
    ",ans); ans=0;
    	}
    	return 0;	
    }
    
  • 相关阅读:
    python2(跳脱字节、字符类型:字符串、boolean)
    php like模糊查询详解 '%value%'
    php mysql distinct关键字的用法
    php mysql 基础的增删改查操作
    for循环 求数组平均数和总数
    for()循环关联数组
    找出数组中的最大值及其索引
    php 2种常用定义数组的用法
    php 使用字符串函数取出数组中的图片名
    php 使用魔术变量加载文件 __DIR__
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/10393862.html
Copyright © 2011-2022 走看看