1. 程式人生 > >【矩陣快速冪】【狀態壓縮】【動態規劃】lydsy4000 [TJOI2015]棋盤

【矩陣快速冪】【狀態壓縮】【動態規劃】lydsy4000 [TJOI2015]棋盤

4000: [TJOI2015]棋盤
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 643 Solved: 314
[Submit][Status][Discuss]
Description
a

Input
輸入資料的第一行為兩個整數N,M表示棋盤大小。第二行為兩個整數P,K,
表示攻擊範圍模板的大小,以及棋子在模板中的位置。接下來三行,
每行P個數,表示攻擊範圍的模版。每個數字後面一個空格。
Output
一個整數,表示可行方案Mod 2 ^32

Sample Input
2 2

3 1

0 1 0

1 1 1

0 1 0
Sample Output
7
HINT
1<=N<=10^6,1<=M<=6


 這兩天學習和程式碼的狀態都很混亂,感覺是一個陣痛期。這題本身不難,當做一個模板記錄題吧。
 題意很顯然(需要注意題目中的行列都是從0開始標號的)。注意到資料範圍n很大而m很小,猜測出是dp,然後可以用1<<m個數字的二進位制代表所有狀態。由於每次狀態轉移方式固定不變,所以可以先預處理出轉移矩陣,再用矩陣快速冪來進行加速。
 預處理時,我們先判斷兩個狀態本身是否合法(同一行不互相攻擊),然後對合法的狀態之間嘗試連邊,使得兩個狀態沒有棋子互相攻擊。
 矩陣乘法時,答案應為AF^n的各個元素和,其中A是一個只有無棋子狀態為1,其餘狀態為0的1(1<<m)的矩陣。為了方便,我們直接把第一行的和求出來就行了。
 另外,題目裡要求對1LL<<32取模,可以直接對unsigned溢位,進一步,可以直接對int溢位,輸出時轉換為unsigned即可,這是因為int是按照補碼儲存的,本質就是一個unsigned。

#include<cstdio>
#define bit(x,i) (x>>i-1&1)
using namespace std;

struct Matrix
{
	int P,Q,M[65][65];
	
	inline void operator = (const Matrix &t) 
	{
		P=t.P;
		Q=t.Q;
		for(int i=1;i<=P;i++)
			for(int j=1;j<=Q;j++)
				M[i][j]=t.M[i][j];
	}
	
	void times(Matrix &a,
Matrix &b) { if(a.Q!=b.P) return ; P=a.P; Q=b.Q; for(int i=1;i<=P;i++) for(int j=1;j<=b.Q;j++) { M[i][j]=0; for(int k=1;k<=a.Q;k++) M[i][j]+=a.M[i][k]*b.M[k][j]; } } void quick_power(int t) { Matrix res[2],base[2]; int o=0,p=0; res[p]=*this; base[o]=*this; --t; while(t) { if(t&1) { res[p^1].times(res[p],base[o]); p^=1; } base[o^1].times(base[o],base[o]); o^=1; t>>=1; } *this=res[p]; } }G; int n,m,p,k,A[4][7]; unsigned ans; bool check_valid(int x) { for(int i=1;i<=m;i++) //列舉棋子對,看是否攻擊 if(bit(x,i)) for(int j=1;j<=m;j++) if(i!=j&&bit(x,j)) if(j-i+k>0&&j-i+k<=p&&A[2][j-i+k]) return false; return true; } bool has_edge(int x, int y) { for(int i=1;i<=m;i++) //看上面是否攻擊了下面 if(bit(x,i)) for(int j=1;j<=m;j++) if(bit(y,j)) if(j-i+k>0&&j-i+k<=p&&A[3][j-i+k]) return false; for(int i=1;i<=m;i++) //看下面是否攻擊了上面 if(bit(y,i)) for(int j=1;j<=m;j++) if(bit(x,j)) if(j-i+k>0&&j-i+k<=p&&A[1][j-i+k]) return false; return true; } int main() { scanf("%d%d%d%d",&n,&m,&p,&k); k++; for(int i=1;i<=3;i++) for(int j=1;j<=p;j++) scanf("%d",&A[i][j]); G.P=G.Q=1<<m; for(int i=1;i<=(1<<m);i++) if(check_valid(i-1)) for(int j=1;j<=(1<<m);j++) if(check_valid(j-1)) G.M[i][j]=has_edge(i-1,j-1); G.quick_power(n); for(int i=1;i<=(1<<m);i++) ans+=G.M[1][i]; printf("%u",ans); return 0; }