1. 程式人生 > 其它 >【題解】中國象棋 - 擺上馬

【題解】中國象棋 - 擺上馬

中國象棋 - 擺上馬

題目大意

給定 \(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;
}