hdu5731
【题意】
给你一个nm的棋盘,
我们用12或2*1(即横着放或竖着放)的骨牌去填充这个棋盘。
问你有多少种填法,使得这个棋盘——
任意相邻两行或两列之间都必须要有一个骨牌横跨。
也就是使得这个棋盘为稳定的。
题解from phile的空间
先求出不考虑分割线的n*m棋盘的覆盖方案数记为f[n][m]
然后枚举列分割线的状态(状压),计算此时不存在行分割线的方案数
求出这个我们就可以用容斥原理算出答案了
怎么算在列分割线确定的情况下,不存在行分割线的方案数呢?
记s[i]=f[i][a1]f[i][a2]…表示在有i行不考虑行分割线且列分割线分成a1,a2…ak块的方案数
则到第i行不存在行分割线的方案数g[i]=s[i]-∑ g[j]*s[i-j] (1<=j<i)
相当于补集转化,求第一条行分割线为j时不合法的方案数……
一开始把答案都预处理出来即可
通过在第一个出现不合法位置来减去不合法,达到不重不漏的目的。是很常用的方法。
本题对列容斥,而对行用这种方法直接算出准确值
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;
const ld inf = 2e18;
const int maxn = 100020;
const ll mod = 1e9 + 7;
ll z[20],ans[20][20],g[20],f[20][20],a[20];
unordered_map <int,ll> h[2];
int now,last;
inline void add(ll &x,ll y){
x += y;
if (x >= mod) x-= mod;
if ( x < 0 ) x += mod;
}
void calc_f(){
for (int n = 1 ; n <= 16 ; n++){
h[0].clear() , h[1].clear();
now = 0; h[now][0] = 1;
for (int i = 1 ; i <= 16 ; i++){
for (int j = 1 ; j <= n ; j++){
last = now , now ^= 1;
h[now].clear();
if ( j == 1 ) f[n][i - 1] = h[last][0];
for (const auto &it : h[last]){
int s = it.fi; ll d = it.se;
if ( j == 1 ){
if ( (s >> n) & 1 ) continue;
s <<= 1;
}
int up = (s >> (j - 1)) & 1 , left = (s >> j) & 1;
int cur = s - (up << (j - 1)) - (left << j);
if ( up && left ) continue;
if ( up || left ){
add(h[now][cur],d);
}
else{ //create
add(h[now][cur + (1 << (j - 1))],d);
add(h[now][cur + (1 << j)],d);
}
}
}
}
f[n][16] = h[now][0];
}
//for (int i = 1 ; i <= 16 ; i++){ for (int j = 1 ; j <= 16 ; j++){ cout<<f[i][j]<<" "; } cout<<endl; }
}
int split(int s,int m){
int k = 0,last = -1;
for (int i = 0 ; i < m - 1 ; i++){
if ( s & (1 << i) ){
a[++k] = i - last;
last = i;
}
}
a[++k] = m - last - 1;
return k;
}
void calc_ans(){
for (int m = 1 ; m <= 16 ; m++){
int S = (1 << (m - 1)) - 1;
for (int s = 0 ; s <= S ; s++){
int k = split(s,m);
for (int n = 1 ; n <= 16 ; n++){
z[n] = 1;
for (int i = 1 ; i <= k ; i++) z[n] = z[n] * f[n][a[i]] % mod;
g[n] = z[n];
for (int i = 1 ; i < n ; i++) g[n] = (g[n] - g[i] * z[n - i] % mod + mod) % mod;
add(ans[n][m],g[n] * (!(k & 1) ? (-1) : 1));
}
}
}
}
void init(){
calc_f();
calc_ans();
}
int main(){
init();
int n,m;
while ( ~scanf("%d %d",&n,&m) ) printf("%lld\n",ans[n][m]);
}
bzoj 1435: [ZJOI2009]多米诺骨牌
本题只是多了障碍的限制,使得需要记录任意子矩阵的覆盖方案数。
bzoj竟然连unordered_map都不支持,编译器真的太老了, 本地过了对拍。懒得写hashmap
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;
const ld inf = 2e18;
const int maxn = 100020;
const int mod = 19901013;
int z[20][20],ans[20][20],g[20],f[20][20][20][20],a[20];
char ch[20][20];
unordered_map <int,int> h[2];
unordered_map <int,int>::iterator it;
int now,last,N,M;
inline void add(int &x,int y){
x += y;
if (x >= mod) x-= mod;
if ( x < 0 ) x += mod;
}
void calc_f(){
for (int l = 1 ; l <= N ; l++) for (int r = l ; r <= N ; r++){
int n = (r - l + 1);
for (int i = 1 ; i <= M ; i++){
h[0].clear() , h[1].clear();
now = 0; h[now][0] = 1;
for (int k = i ; k <= M ; k++){
for (int j = l ; j <= r ; j++){
last = now , now ^= 1;
h[now].clear();
if ( j == l ) f[l][r][i][k - 1] = h[last][0];
for (it = h[last].begin() ; it != h[last].end() ; ++it){
int s = (*it).fi; int d = (*it).se;
if ( j == l ){
if ( (s >> n) & 1 ) continue;
s <<= 1;
}
int up = (s >> (j - l)) & 1 , left = (s >> (j - l + 1)) & 1;
int cur = s - (up << (j - l)) - (left << (j - l + 1));
if ( up && left ) continue;
if ( up || left ){
if ( ch[j][k] == '.' ) add(h[now][cur],d);
}
else{ //create
if ( ch[j][k] == '.' ){
add(h[now][cur + (1 << (j - l))],d);
add(h[now][cur + (1 << (j - l + 1))],d);
}
add(h[now][cur],d);
}
}
}
}
f[l][r][i][M] = h[now][0];
}
}
//for (int i = 1 ; i <= N ; i++){ for (int j = 1 ; j <= M ; j++){ cout<<f[i][j]<<" "; } cout<<endl; }
}
int split(int s,int m){
int k = 0,last = -1;
for (int i = 0 ; i < m - 1 ; i++){
if ( s & (1 << i) ){
a[++k] = i - last;
last = i;
}
}
a[++k] = m - last - 1;
return k;
}
void calc_ans(){
int m = M;
int S = (1 << (m - 1)) - 1;
for (int s = 0 ; s <= S ; s++){
int k = split(s,m);
// cout<<"split\n";
// rep(i,1,k) cout<<a[i]<<" ";
// cout<<endl;
for (int n = 1 ; n <= N ; n++){
//z[n] = 1;
for (int l = 1 ; l <= n ; l++){
z[l][n] = 1;
int cur = 1;
for (int i = 1 ; i <= k ; i++) z[l][n] = (ll)z[l][n] * f[l][n][cur][cur + a[i] - 1] % mod , cur += a[i];
}
g[n] = z[1][n];
for (int i = 1 ; i < n ; i++) g[n] = (g[n] - (ll)g[i] * z[i + 1][n] % mod + mod) % mod;
add(ans[n][m],g[n] * (!(k & 1) ? (-1) : 1));
}
// for (int i = 1 ; i <= N ; i++){
// cout<<i<<" g : "<<g[i]<<endl;
// for (int j = 1 ; j <= i ; j++) cout<<z[i][j]<<" ";
// cout<<endl;
// }
}
}
void init(){
calc_f();
calc_ans();
}
int main(){
freopen("input.txt","r",stdin);
scanf("%d %d",&N,&M);
for (int i = 1 ; i <= N ; i++) scanf("%s",ch[i] + 1);
init();
printf("%d\n",ans[N][M]);
}