kmjp's blog

競技プログラミング参加記です

yukicoder : No.621 3 x N グリッド上のドミノの置き方の数

これはここまでの類題解いてれば解けるね。
https://yukicoder.me/problems/no/621

問題

3*Nのグリッド上に、1*2の大きさのドミノを互いに重ならない範囲でこれ以上置けなくなるまで置く。
その場合の置き方の組み合わせは何通りあるか求める。

解法

左からk列までの埋め方が確定した状態で、k+1列目をどうするか考えよう。
その際、左から(k-1)列目とk列目の計6マスの状態が決まっていると、(k+1)列目の取り方の候補が定まる。
よってk列目まで埋めたときの組み合わせを、右2列の状態に応じ64次元ベクトルで考えると、そこから(k+1)列目がどうなるかは64次正方行列を掛ければ定まることになる。

あとは頑張ってそのような状態遷移に関する正方行列を作り、行列累乗しよう。
以下は頑張ってDFSして(k+1)列目の状態遷移を求めている。

ll mo=1000000007;

const int MAT=64;
struct Mat { ll v[MAT][MAT]; };
Mat mulmat(Mat& a,Mat& b,int n=MAT) {
	ll mo2=4*mo*mo;
	int x,y,z; Mat r;
	FOR(x,n) FOR(y,n) r.v[x][y]=0;
	FOR(x,n) FOR(z,n) FOR(y,n) {
		r.v[x][y] += a.v[x][z]*b.v[z][y];
		if(r.v[x][y]>mo2) r.v[x][y] -= mo2;
	}
	FOR(x,n) FOR(y,n) r.v[x][y]%=mo;
	return r;
}

Mat powmat(ll p,Mat a,int n=MAT) {
	int i,x,y; Mat r;
	FOR(x,n) FOR(y,n) r.v[x][y]=0;
	FOR(i,n) r.v[i][i]=1;
	while(p) {
		if(p%2) r=mulmat(r,a,n);
		a=mulmat(a,a,n);
		p>>=1;
	}
	return r;
}

ll modpow(ll a, ll n = mo-2) {
	ll r=1;
	while(n) r=r*((n%2)?a:1)%mo,a=a*a%mo,n>>=1;
	return r;
}

ll N;
Mat A,B;

void dfs(int id,int cmask,int row) {
	if(row==0) {
		if((cmask&((1<<3)|(1<<6)))==0) dfs(id,cmask|(1<<3)|(1<<6),1);
		dfs(id,cmask|(1<<6)|(1<<7),2);
		dfs(id,cmask,1);
	}
	if(row==1) {
		if((cmask&((1<<4)|(1<<7)))==0) dfs(id,cmask|(1<<4)|(1<<7),2);
		dfs(id,cmask|(1<<7)|(1<<8),3);
		dfs(id,cmask,2);
	}
	if(row==2) {
		if((cmask&((1<<5)|(1<<8)))==0) dfs(id,cmask|(1<<5)|(1<<8),3);
		dfs(id,cmask,3);
	}
	if(row==3) {
		if((cmask&9)==0) return;
		if((cmask&18)==0) return;
		if((cmask&36)==0) return;
		if((cmask&24)==0) return;
		if((cmask&48)==0) return;
		A.v[cmask>>3][id]++;
	}
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N;
	
	FOR(i,64) {
		if((i&3)==0) continue;
		if((i&6)==0) continue;
		dfs(i,i,0);
		//FOR(j,64) cout<<A.v[i][j]<<" ";
		//cout<<endl;
	}
	B=powmat(N,A);
	ll ret=0;
	FOR(i,64) {
		if((i&3)==0) continue;
		if((i&6)==0) continue;
		if((i&24)==0) continue;
		if((i&48)==0) continue;
		if((i&9)==0) continue;
		if((i&18)==0) continue;
		if((i&36)==0) continue;
		ret+=B.v[i][63];
	}
	cout<<ret%mo<<endl;
	
	
	
}

まとめ

3*Nグリッド問題はぼちぼち打ち止めだろうか…?