Gym101635C Macarons
题目链接:http://codeforces.com/gym/101635/attachments
题目大意:
给出一个 \(N \times M\) 的网格图,请你用 \(1 \times 1\) 和 \(1 \times 2\) 两种纸片填满该图,问有几种方案。
知识点: 矩阵快速幂、DFS
解题思路:
做这道题如果没有思路的话,建议先看看 \(Matrix67\) 的这篇文章。这道题就是其中的经典题目九的变形。
由于列数很少,所以我们可以利用列与列之间的状态转移。
设矩阵 \(Mat[state1][state2]\),其代表填满当前列(当前列的初始状态为\(state1\))并且使得下一列状态为 \(state2\) 的方案数(每一列的状态无非就是这一列哪些格子已经被填,哪些还没被填,我们可以用一个二进制数来表示状态)。
如此一来,我们就可以先用 \(dfs\) 来求出从第一列的各种状态转移到第二列的各种状态的方案数,然后求出 \(Mat\) 的 \(M\) 次幂 \(ans\) ,答案即为 \(ans[0][0]\).
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const ll mod=1e9; 5 struct Matrix { 6 int mat[260][260]; 7 }; 8 Matrix Multiply(Matrix x, Matrix y, int n) { 9 Matrix temp; 10 memset(temp.mat, 0, sizeof(temp.mat)); 11 for (int i = 0; i < n; i++) { 12 for (int j = 0; j < n; j++) { 13 for (int k = 0; k < n; k++) { 14 ll tmp=(ll)x.mat[i][k] * y.mat[k][j]%mod; 15 temp.mat[i][j] = (int)(((ll)temp.mat[i][j]+tmp)%mod); 16 } 17 } 18 } 19 return temp; 20 } 21 Matrix Fast_Power(Matrix a, ll m, int n) { 22 Matrix res; 23 memset(res.mat, 0, sizeof(res.mat)); 24 for (int i = 0; i < n; i++) res.mat[i][i] = 1; 25 26 while (m) { 27 if (m & 1) res = Multiply(res, a, n); 28 m >>= 1; 29 a = Multiply(a, a, n); 30 } 31 return res; 32 } 33 34 Matrix ans; 35 void dfs(int org,int now,int nex,int n){ 36 int one[10],two[10]; 37 memset(one,0,sizeof(one)); 38 memset(two,0,sizeof(two)); 39 int tnex=nex,tnow=now; 40 int ind=0; 41 while(now){ 42 one[ind]=now%2; 43 now>>=1; 44 ind++; 45 } 46 bool flag=true; 47 for(int i=0;i<n;i++){ 48 if(one[i]!=1){ 49 flag=false; 50 break; 51 } 52 } 53 54 if(flag){ 55 ans.mat[org][tnex]++; 56 return; 57 } 58 ind=0; 59 while(nex){ 60 two[ind]=nex%2; 61 nex>>=1; 62 ind++; 63 } 64 65 for(int i=0;i<n;i++){ 66 if(one[i]==0){ 67 dfs(org,tnow+(1<<i),tnex,n); 68 dfs(org,tnow+(1<<i),tnex|(1<<i),n); 69 if(i+1<n&&one[i+1]==0){ 70 dfs(org,tnow+(1<<i)+(1<<(i+1)),tnex,n); 71 }//为了避免重复,我们不在下一列放置 2*1 的纸片 72 break; 73 } 74 } 75 } 76 int main(){ 77 // freopen("in.txt","r",stdin); 78 int N; 79 ll M; 80 scanf("%d%lld",&N,&M); 81 for(int i=0;i<(1<<N);i++) 82 dfs(i,i,0,N); 83 ans=Fast_Power(ans,M,(1<<N)); 84 printf("%d\n",ans.mat[0][0]); 85 return 0; 86 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”