【HDU5731 2016 Multi-University Training Contest 1I】【轮廓线DP+容斥】Solid Dominoes Tilings nm棋盘1x2多边形填充稳定方案
2016-07-25 10:00
417 查看
Solid Dominoes Tilings
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 164 Accepted Submission(s): 100
[align=left]Problem Description[/align]
Dominoes are rectangular tiles with nice 2 × 1 and 1 × 2 sizes.
The tiling is called solid if it is not possible to split the tiled rectangle by a straight line, not crossing the interior of any tile. For example, on the picture below the tilings (a) and (b) are solid, while the tilings (c) and (d) are not.
Now the managers of the company wonder, how many different solid tilings exist for an m × n rectangle. Help them to find that out.
[align=left]Input[/align]
The input file contains m and n(1≤m,n≤16).
[align=left]Output[/align]
Output one integer number mod 1e9+7 - the number of solid tilings of m×n rectangle with 2 × 1 and 1 × 2 pavement tiles.
[align=left]Sample Input[/align]
2 2
5 6
8 7
[align=left]Sample Output[/align]
0
6
13514
Hint
All solid tilings for the 5×6 rectangle are provided on the picture below:
[align=left]Author[/align]
HIT
[align=left]Source[/align]
2016 Multi-University Training
Contest 1
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
void add(int &x, int y)
{
if ((x += y) >= Z)x -= Z;
}
//基础DP
int dp[2][1 << 16];
int basic[17][17]; //basic[i][j]表示在不考虑"稳定"的情况下,填充i*j棋盘的方案数
void basicdp()
{
for (int m = 1; m <= 16; ++m)//枚举宽
{
int top = (1 << m) - 1;
int now = 0, nxt = 1;
MS(dp[now], 0); dp[now][top] = 1;
for (int i = 0; i < m; ++i)//逐行DP
{
for (int j = 0; j < m; ++j)//逐列DP
{
MS(dp[nxt], 0);
for (int sta = 0; sta <= top; ++sta)
{
//横放
if (j && !(sta & 1 << j - 1) && (sta & 1 << j))
{
add(dp[nxt][sta | 1 << j - 1], dp[now][sta]);
}
//竖放 && 不放
add(dp[nxt][sta ^ 1 << j], dp[now][sta]);
}
swap(now, nxt);
}
basic[i + 1][m] = basic[m][i + 1] = dp[now][top];
}
}
}
//容斥DP
int v[17]; //在给定列分隔条件下的无行分割的方案计数
int w[17]; //在给定列分隔条件下的有行分割的方案计数
int p[17]; //列分隔长度
int ans[17][17];
void IEdp()
{
MS(ans, 0);
for (int m = 1; m <= 16; ++m)//枚举宽
{
int top = (1 << m - 1) - 1;
for (int sta = 0; sta <= top; ++sta)//枚举列分割状态
{
int num = 0;
for (int i = 0; i < m - 1; ++i)if (sta >> i & 1)p[++num] = i + 1;
p[++num] = m;
for (int i = num; i >= 2; --i)p[i] -= p[i - 1];
for (int i = 1; i <= 16; ++i)//逐行DP
{
LL val = 1;
for (int j = 1; j <= num; ++j)val = val * basic[i][p[j]] % Z;
w[i] = v[i] = val;
for (int y = 1; y < i; ++y)add(v[i], Z - (LL)v[y] * w[i - y] % Z);
if (num & 1)add(ans[i][m], v[i]);
else add(ans[i][m], Z - v[i]);
}
}
}
//复杂度2 ^ 16(枚举列分割状态) * C(16,2)(枚举当前行和之前行) * 2(不同个宽度)
//大概是3e7的复杂度,可以无压力AC。
}
int main()
{
basicdp();
IEdp();
int n, m;
while (~scanf("%d%d", &n, &m))
{
printf("%d\n", ans
[m]);
}
return 0;
}
/*
【trick&&吐槽】
做题多就是好。
这道题为——
[稳定多米诺骨牌]:http://www.51nod.com/onlineJudge/problemSolution.html#!problemId=1518
同样也是——
BZOJ 1435 [ZJOI2009]多米诺骨牌:
【题意】
给你一个n*m的棋盘,
我们用1*2或2*1(即横着放或竖着放)的骨牌去填充这个棋盘。
问你有多少种填法,使得这个棋盘——
任意相邻两行或两列之间都必须要有一个骨牌横跨。
也就是使得这个棋盘为稳定的。
【类型】
轮廓线DP
【分析】
如果没有"稳定的"这一限制,这题就变成了最基础的轮廓线DP。
这道题的解题步骤是这样子的——
1,基础轮廓线DP
2,容斥
关键是如何容斥。
一开始,我所设想的容斥方法是——
先通过二进制枚举哪些列是断开的,然后通过"奇减偶加"的原则,求出对于n*m的矩形,在列不分割条件下的方案数。
然后再逐行枚举,做行容斥——
使得矩形分成上下两块,上面一块内部无行分割,下面一块行分割任意,且上下两块都无列分割
容斥减去(上方案数*下方案数)
不过,这种容斥是错误的。
我这样容斥掉的,是上下都无列分割的方案数。
但是显然,我上下单独的一块,也许是可以有列分割的。
只是在其合并之后没有了列分割。
那要怎么办?
我可以——
先枚举行,再枚举列,然后枚举列分割的状态。
在我们已知列分割状态的基础上,
求出对于长为n,宽为m的图形的合法分割数——
显然,该合法分割数为——
对于分割的列数——奇加偶减
{
内部的容斥是——
枚举上下两部分
上部分内部无行分割
下部分内部随意行分割
计数相乘
}
这里考虑了完整的行列分割状态的影响,所以可以正确AC。
至于复杂度,是有一点儿炸就是了233。
【时间复杂度&&优化】
O(3e7)
*/
相关文章推荐
- [HAOI2010][DP]最长公共子序列
- HDU 1695(容斥+欧拉函数+素数分解)
- hdu 5212(容斥)
- hdu 5468 Puzzled Elena 预处理+深搜+容斥
- 【BZOJ3622】已经没有什么好害怕的了
- 【SDOI2015】【BZOJ4086】旅行计划travel
- codeforces 630 K. Indivisibility
- DHU 4135 co-primer(容斥原理)
- hdu 4135 Co-prime 复习容斥
- hdu 1695 GCD 容斥 欧拉函数预处理
- hdu5072 coprime 容斥 和睦三元组
- BNUOJ 51637 Quailty's Life | BZOJ 4587 推箱子
- SGU 537 Divisibility
- 【Codeforces Round 324 (Div 2)B】【容斥】Kolya and Tanya 环上n个3元组至少有一组和不为6
- 【HDU5565 BestCoder Round 62 (div1)C】【STL or 二分答案 or 计数排序】Clarke and baton n个人减肥m次求最后异或值
- 【HDU5564 BestCoder Round 62 (div1)B】【DP转矩阵快速幂】Clarke and digits 长度在[l,r]范围内7倍数数个数要求相邻两位不为K
- 【HDU5544 2015CCPC 南阳国赛E】【树上dfs找本质不同环 高斯消元 时间戳优化】Ba Gua Zhen 连通图上最大异或环
- 【HDU5222 2015赛码冠军杯I】【并查集找双连通 + tarjan求强连通】Exploration 双向边只能走一边是否图上存在环
- 【HDU5188 BestCoder Round 33C】【贪心排序+DP】zhx and contest 考试不被怀疑作弊条件下达到至少m分的最少时间
- 【HDU4560 2013西山居复赛D】【二分答案+网络流拆点】我是歌手 安排演唱会_每人歌不同_每场歌不同_人歌匹配一次