原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1518.html

题目传送门 - 51Nod1518

题意

51Nod真是个好OJ ,题意概括的真好,有助于博主偷懒不写题意概括。给51Nod 点赞!

题解

  首先,我们忽略那个“稳定”的要求,求方案数。

  显然是一个插头dp裸题,我们可以在 $O(n^2\cdot 2^n)$ 的时间复杂度中求出所有长宽的矩形区域的覆盖方案数。

  然后我们考虑容斥原理,奇加偶减。首先,枚举哪些相邻行之间有一条不穿过骨牌的直线,然后,用一个 $O(n)$ DP 来解决相邻列之间分割线的容斥。

  总的时间复杂度 $O(n^22^n)$ 。打出表之后,询问 $O(1)$ 。

代码

看着那些运行效率榜上15MS的代码我于是交了一份 0MS 的代码。正常的代码在这份代码之后。

#include <bits/stdc++.h>
int n,m,ans[17][17]={
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,6,0,108,0,1182,0,10338,0,79818,0,570342},
{0,0,0,0,0,6,0,124,62,1646,1630,18120,25654,180288,317338,1684956,3416994},
{0,0,0,0,0,0,124,0,13514,0,765182,0,32046702,0,136189727,0,378354090},
{0,0,0,0,0,108,62,13514,25506,991186,3103578,57718190,238225406,965022920,388537910,937145938,315565230},
{0,0,0,0,0,0,1646,0,991186,0,262834138,0,462717719,0,560132342,0,699538539},
{0,0,0,0,0,1182,1630,765182,3103578,262834138,759280991,264577134,712492587,886997066,577689269,510014880,807555438},
{0,0,0,0,0,0,18120,0,57718190,0,264577134,0,759141342,0,567660301,0,47051173},
{0,0,0,0,0,10338,25654,32046702,238225406,462717719,712492587,759141342,398579168,83006813,821419653,942235780,558077885},
{0,0,0,0,0,0,180288,0,965022920,0,886997066,0,83006813,0,690415372,0,620388364},
{0,0,0,0,0,79818,317338,136189727,388537910,560132342,577689269,567660301,821419653,690415372,796514774,696587391,175421667},
{0,0,0,0,0,0,1684956,0,937145938,0,510014880,0,942235780,0,696587391,0,856463275},
{0,0,0,0,0,570342,3416994,378354090,315565230,699538539,807555438,47051173,558077885,620388364,175421667,856463275,341279366}
};
int main(){
while (~scanf("%d%d",&n,&m))
printf("%d\n",ans[n][m]);
return 0;
}

  

正常的代码

#include <bits/stdc++.h>
using namespace std;
int read(){
int x=0;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+ch-48,ch=getchar();
return x;
}
const int N=17,S=1<<16,mod=1e9+7;
int n,m,dp[2][S],tot[N][N],ans[N][N];
int gbit(int v,int d){
return (v>>(d-1))&1;
}
void Solve_tot(int n,int m){
memset(dp,0,sizeof dp);
int T0=1,T1=0;
dp[T1][(1<<m)-1]=1;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
T0^=1,T1^=1;
memset(dp[T1],0,sizeof dp[T1]);
for (int s=0;s<(1<<m);s++){
int v=dp[T0][s];
if (!v)
continue;
dp[T1][s^(1<<(j-1))]=(dp[T1][s^(1<<(j-1))]+v)%mod;
if (j>1&&!gbit(s,j-1)&&gbit(s,j)){
int _s=s^(1<<(j-2));
dp[T1][_s]=(dp[T1][_s]+v)%mod;
}
}
}
tot[i][m]=dp[T1][(1<<m)-1];
}
}
void Get_tot(int n){
for (int m=1;m<=16;m++)
Solve_tot(n,m);
}
int GetV(int n,int s,int len){
int v=1;
for (int i=1,j;i<=n;i=j){
for (j=i;j<n&&!((s>>j)&1);j++);
j++;
v=1LL*v*tot[j-i][len]%mod;
}
return v;
}
int cnt_1(int v){
int ans=0;
while (v)
ans+=v&1,v>>=1;
return ans;
}
void Solve_ans(int n,int m){
int dp[N],v[N];
for (int s=0;s<(1<<n);s++){
if (!(s&1))
continue;
memset(dp,0,sizeof dp);
for (int i=1;i<=m;i++)
v[i]=GetV(n,s,i);
dp[0]=1;
for (int i=1;i<=m;i++)
for (int j=0;j<i;j++)
dp[i]=(-1LL*dp[j]*v[i-j]+dp[i])%mod;
int f=(cnt_1(s)&1)?-1:1;
for (int i=1;i<=m;i++)
ans[n][i]=(ans[n][i]+f*dp[i])%mod;
}
}
void Get_ans(int m){
memset(ans,0,sizeof ans);
for (int n=1;n<=16;n++)
Solve_ans(n,m);
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
ans[i][j]=(ans[i][j]+mod)%mod;
}
int main(){
Get_tot(16);
Get_ans(16);
while (~scanf("%d%d",&n,&m))
printf("%d\n",ans[n][m]);
return 0;
}

  

51Nod1518 稳定多米诺覆盖 动态规划 插头dp 容斥原理的更多相关文章

  1. 【做题】51NOD1518 稳定多米诺覆盖——容斥&dp

    题意:求有多少种方案,用多米诺骨牌覆盖一个\(n\times m\)的棋盘,满足任意一对相邻行和列都至少有一个骨牌横跨.对\(10^9+7\)取模. \(n,m \leq 16\) 首先,这个问题的约 ...

  2. 51nod 1518 稳定多米诺覆盖(容斥+二项式反演+状压dp)

    [传送门[(http://www.51nod.com/Challenge/Problem.html#!#problemId=1518) 解题思路 直接算不好算,考虑容斥,但并不能把行和列一起加进去容斥 ...

  3. P1282 多米诺骨牌 (差值DP+背包)

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  4. bzoj1814: Ural 1519 Formula 1 动态规划 插头dp

    http://acm.timus.ru/problem.aspx?space=1&num=1519 题目描述 一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数. ...

  5. 洛谷P1282 多米诺骨牌【线性dp】

    题目:https://www.luogu.org/problemnew/show/P1282 题意: 给定n个牌,每个牌有一个上点数和下点数.可以通过旋转改变交换上下点数. 问使得上点数之和和下点数之 ...

  6. ACM - 动态规划 - P1282 多米诺骨牌

    多米诺骨牌由上下 \(2\) 个方块组成,每个方块中有 \(1 \sim 6\) 个点.现有排成行的上方块中点数之和记为 \(S_1\),下方块中点数之和记为 \(S_2\),它们的差为 \(\lef ...

  7. 用1 x 2的多米诺骨牌填满M x N矩形的方案数(完美覆盖)

    题意 用 $1 \times 2$ 的多米诺骨牌填满 $M \times N$ 的矩形有多少种方案,$M \leq 5,N < 2^{31}$,输出答案模 $p$. 分析 当 $M=3$时,假设 ...

  8. Luogu P2595 [ZJOI2009]多米诺骨牌 容斥,枚举,插头dp,轮廓线dp

    真的是个好(毒)题(瘤).其中枚举的思想尤其值得借鉴. \(40pts\):插头\(dp\),记录插头的同时记录每一列的连接状况,复杂度\(O(N*M*2^{n + m} )\). \(100pts\ ...

  9. [CareerCup] 6.2 Dominos on Chess Board 棋盘上的多米诺

    6.2 There is an 8x8 chess board in which two diagonally opposite corners have been cut off. You are ...

随机推荐

  1. Ubuntu选择软件源

    1. 系统桌面右上角,系统菜单中选择Software Up to Date 2. 点击Settings-按钮 3. Ubuntu Software标签页,Download from选择中国的软件服务器 ...

  2. Uva 11178 Morley定理

    题意: 给你三角形三个点, 定理是 三个内角的三等分线相交得出 DEF三点, 三角新 DFE是等边三角形 然后要你输出 D E F 的坐标 思路 : 求出三个内角,对于D 相当于 BC向量逆时针旋转, ...

  3. 【原创】大叔经验分享(40)hdfs关闭kerberos

    hadoop.security.authentication: Kerberos -> Simple hadoop.security.authorization: true -> fals ...

  4. Django 笔记(五)自定义标签 ~ 映射mysql

    创建简单标签: 1-3 或者 2-3 创建包含标签: 1-2或1-3或1-4(推荐) 包含标签使用: 2 和 4对应上面的使用方法 在虚拟环境中安装: pip install pymysql 在set ...

  5. git 创建分支

  6. Idea-Java接入银联支付的Demo

    注:本文来源于:< Idea-Java接入银联支付的Demo > 接入银联支付的Demo,希望能给大家节约一点时间 https://github.com/wangfei0904306/un ...

  7. Linux端BaiduPCS-Go使用方法

    下载https://pan.baidu.com/s/1RFHTRE1c_JlP8rrZiERsTg 运行 ./BaiduPCS-Go 可能更新:update 登录:login 下载: d xxx 更多 ...

  8. nginx实践(五)之代理服务(正向代理与反向代理介绍)

    正向代理 正向代理代理是为客户端服务,代理负责DNS解析域名到对应ip,并进行访问服务端,返回响应给客户端 反向代理 客户端自己负责请求DNS解析域名到对应ip,服务端通过代理分发流量,进行负载均衡 ...

  9. Android adb from work ---three

    转接from work --two 0.查看Android的物理存储空间df adb shell df -h 表示以MB单位查看存储使用情况 1.在Android设备中创建指定大小的文件(也能测试其I ...

  10. selenium 获取input输入框中的值的方法

    方法一:获取input的文本值 <input class="form-text-normal" id="txtName" name="Name& ...