【題解】中國象棋 - 擺上馬
題目大意
給定 \(n\times m\) 的棋盤,往棋盤上面擺馬,問有多少種擺法可以讓馬互不攻擊。馬是中國象棋的馬,是可以被阻擋攻擊路線的,如題乾圖中所示。
題目解法
看到資料範圍,首先想到的就是狀壓了。我們發現,馬涉及的狀態實際上是 前兩列 ,所以我們需要存下前兩列的狀態。
那麼我們就有一個自然的狀態設計:\(f[i][j][k]\) 表示第 \(i\) 行,前兩列的狀態是 \(j,k\) 的當前行的方案數。轉移順序很顯然直接從上往轉移即可。
下面我們從初始化開始分析這個 \(dp.\)
對於第一行,顯然狀態只有 \(f[1][0][0]=2^m,\) 直接賦值即可。
對於第二行,考慮直接暴力處理相鄰兩行的所有可能性,複雜度 \(O(4^m),\)
如果可以攻擊到,那麼需要滿足它沒有被阻擋並且攻擊的位置上有馬。簡單位運算計算一下即可。
bool check(int x,int y){ for(int i=2;i<m;++i){ if(!(y>>i&1))continue; int v=(y>>(i-1))&1; if(v)continue; if(x>>(i-2)&1)return false; } for(int i=2;i<m;++i){ if(!(x>>i&1))continue; int v=(x>>(i-1)&1); if(v)continue; if(y>>(i-2)&1)return false; } for(int i=0;i<m-2;++i){ if(!(y>>i&1))continue; int v=(y>>(i+1)&1); if(v)continue; if(x>>(i+2)&1)return false; } for(int i=0;i<m-2;++i){ if(!(x>>i&1))continue; int v=(x>>(i+1)&1); if(v)continue; if(y>>(i+2)&1)return false; } return true; }
繼續思考,到這一步為止我們解決了初始化的問題。考慮下面如何 \(dp:\)
一個顯然的思路是,列舉第幾行,列舉上一行、上上行、上上上行的狀態,然後考慮當期行填什麼。
由於當前行能填多少種東西只會被前兩行影響,所以這一部分也可以直接預處理,複雜度 \(O(8^m).\)
接下來就遇到一個小問題,我們不知道如何繼承之前的狀態了。由於我們設計的狀態不包含當前行的具體狀態,所以我們不能直接獲得對於前兩行狀態分別是 \(j,k\) 時的方案數。
但是,我們可以直接列舉上上上行的狀態,我們發現,如果這個狀態可以滿足當期列舉的後兩行狀態,那麼對於前一行填什麼,它繼承的資訊都是一樣的。也就是說,我們可以直接用這個資訊除掉當前情況上一行能填的方案數,就可以得到固定狀態的上一行的填馬方案數。
至於三行的馬怎麼判斷合法,先把相鄰的馬判斷掉,那麼隔一行的馬也很好判斷,只有兩種情況:要麼上面的馬走到下面攻擊,要麼下面的馬到上面攻擊。直接和兩種馬一樣判斷即可。
bool check(int x,int y,int z){
for(int i=0;i<m;++i){
if(y>>i&1)continue;
if(!(z>>i&1))continue;
if(i&&(x>>(i-1)&1))return false;
if(i<m-1&&(x>>(i+1)&1))return false;
}
for(int i=0;i<m;++i){
if(y>>i&1)continue;
if(!(x>>i&1))continue;
if(i&&(z>>(i-1)&1))return false;
if(i<m-1&&(z>>(i+1)&1))return false;
}
return true;
}
然後這個除法在模意義下直接預處理逆元即可,複雜度 \(O(2^m\log \bmod)\)
總 \(dp\) 複雜度是 \(O(n\times 8^m).\) 寫完發現爆空間了。
由於行對我們的狀態影響不大,直接滾動陣列即可。
複雜度 \(O(n\times 2^8).\)
#include<bits/stdc++.h>
using namespace std;
typedef double db;
//#define int long long
const int mod=1e9+7;
const db eps=1e-14;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
inline db Max(db x,db y){return x-y>eps?x:y;}
inline db Min(db x,db y){return x-y<eps?x:y;}
inline int Add(int x,int y,int M=mod){return (x+y)%M;}
inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
inline int Abs(int x){return x<0?-x:x;}
inline int read(){
int s=0,w=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
inline void write(int x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline int qpow(int x,int y){
int res=1;
while(y){
if(y&1)res=Mul(res,x);
x=Mul(x,x);y>>=1;
}
return res;
}
typedef pair<int,int> pr;
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
namespace Refined_heart{
int f[2][1<<6][1<<6],n,m,val[1<<6][1<<6],ival[1<<6][1<<6];
bool check(int x,int y){
for(int i=2;i<m;++i){
if(!(y>>i&1))continue;
int v=(y>>(i-1))&1;
if(v)continue;
if(x>>(i-2)&1)return false;
}
for(int i=2;i<m;++i){
if(!(x>>i&1))continue;
int v=(x>>(i-1)&1);
if(v)continue;
if(y>>(i-2)&1)return false;
}
for(int i=0;i<m-2;++i){
if(!(y>>i&1))continue;
int v=(y>>(i+1)&1);
if(v)continue;
if(x>>(i+2)&1)return false;
}
for(int i=0;i<m-2;++i){
if(!(x>>i&1))continue;
int v=(x>>(i+1)&1);
if(v)continue;
if(y>>(i+2)&1)return false;
}
return true;
}
int calc(int state){
int res=0;
for(int i=0;i<(1<<m);++i)res+=check(i,state);
return res;
}
bool check(int x,int y,int z){
for(int i=0;i<m;++i){
if(y>>i&1)continue;
if(!(z>>i&1))continue;
if(i&&(x>>(i-1)&1))return false;
if(i<m-1&&(x>>(i+1)&1))return false;
}
for(int i=0;i<m;++i){
if(y>>i&1)continue;
if(!(x>>i&1))continue;
if(i&&(z>>(i-1)&1))return false;
if(i<m-1&&(z>>(i+1)&1))return false;
}
return true;
}
void treat(){
for(int i=0;i<(1<<m);++i)
for(int j=0;j<(1<<m);++j){
if(!check(i,j))continue;
for(int k=0;k<(1<<m);++k){
if(!check(j,k))continue;
val[i][j]+=check(i,j,k);
}
}
for(int i=0;i<(1<<m);++i)
for(int j=0;j<(1<<m);++j)
ival[i][j]=qpow(val[i][j],mod-2);
}
void MAIN(){
n=read();m=read();
f[1][0][0]=1<<m;
for(int i=0;i<(1<<m);++i)f[0][i][0]=calc(i);
treat();
for(int i=3;i<=n;++i){
int pos=i&1;
memset(f[pos],0,sizeof f[pos]);
for(int j=0;j<(1<<m);++j){
for(int k=0;k<(1<<m);++k){
if(!check(k,j))continue;
int s=0;
for(int l=0;l<(1<<m);++l){
if(!check(l,k))continue;
if(!check(l,k,j))continue;
s=Add(s,Mul(f[pos^1][k][l],ival[l][k]));
}
f[pos][j][k]=Add(f[pos][j][k],Mul(s,val[k][j]));
}
}
}
int ans=0;
for(int i=0;i<(1<<m);++i)
for(int j=0;j<(1<<m);++j)
ans=Add(ans,f[n&1][i][j]);
write(ans);
}
}
int main(){
Refined_heart::MAIN();
return 0;
}