https://vjudge.net/problem/UVA-11741
题目
给一个$n$行$m$列的棋盘,上面有一些坏格子(最多100个),剩下的都是好格子,你有充足的$1 imes2$骨牌,问把所有好格子都不重不漏地覆盖同时不能覆盖坏格子的方法有多少个,$1leqslant nleqslant 4, 1leqslant mleqslant 100000000$,输出对10000007取模后的结果。
题解
第一次我直接轮廓线dp到末尾,然后就TLE了……
因为坏格子比较少,行数也很少,因此把行压成一个状态,计算两行状态之间的转移,得到一个矩阵,然后就可以通过矩阵幂计算了……
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cctype> #define REP(i,a,b) for(register unsigned i=(a); i<(b); i++) #define REPE(i,a,b) for(register unsigned i=(a); i<=(b); i++) #define PERE(i,a,b) for(register unsigned i=(a); i>=(b); i--) using namespace std; typedef long long ll; struct node { int a, b; bool operator<(const node&r) const { return b<r.b; } }; #define MAXN 107 #define MO 10000007 node bdcol[MAXN];//记录坏格子 int dp[2][16];//计算转移用的 int mtx[5][16][16];//转移矩阵 int ones[5][16][16];//单位矩阵 inline void db() { memset(ones,0,sizeof(ones)); REP(r,1,5) { unsigned msk=1<<r, m=msk>>1; REP(i,0,msk) { ones[r][i][i]=1; } REP(f,0,msk) { memset(dp[0],0,sizeof(dp[0])); dp[0][f]=1; int now=0; REP(i,0,r) { now^=1; memset(dp[now],0,sizeof(dp[now])); REP(k,0,msk) { if(k&1) dp[now][k>>1]+=dp[now^1][k]; if(!(k&1)) dp[now][(k>>1)|m]+=dp[now^1][k]; if(i && (k&1) && !(k&m)) dp[now][((k|m)>>1)|m]+=dp[now^1][k]; } } REP(t,0,msk) { mtx[r][f][t]=dp[now][t]; } } } } int r,c,n; unsigned msk, m; inline void mul(const int a[16][16], const int b[16][16], int ret[16][16]) { //矩阵乘法 int ans[16][16]; REP(i,0,msk) { REP(j,0,msk) { ans[i][j]=0; REP(k,0,msk) { ans[i][j]=(ans[i][j]+((ll)a[i][k]*b[k][j]))%MO; } } } memcpy(ret,ans,sizeof(ans)); } inline void qpow(const int a[16][16], int y, int ret[16][16]) { //矩阵幂 int ans[16][16]; int x[16][16]; memcpy(x,a,sizeof(x)); memcpy(ans,ones[r],sizeof(ans)); for(;y;y>>=1) { if(y&1) mul(ans,x,ans); mul(x,x,x); } memcpy(ret,ans,sizeof(ans)); } int A[16][16]; int B[16][16]; int main() { int kase=1; db(); while(~scanf("%d%d%d", &r, &c, &n) && r) { msk = 1u<<r; m=msk>>1; REP(i,0,n) { scanf("%d%d", &bdcol[i].a, &bdcol[i].b); } REP(i,0,r) { bdcol[n].a=i, bdcol[n].b=c; n++; } sort(bdcol,bdcol+n); int now=0, from=-1;//当前坏格子编号和列 memcpy(A,ones[r],sizeof(A)); int i=0; while(i<n) { now=i; unsigned k=0; while(i<n && bdcol[i].b==bdcol[now].b) {k|=1<<bdcol[i].a; i++;} qpow(mtx[r], bdcol[now].b-from, B); //B=F^{...} mul(A,B,A); memset(B,0,sizeof(B)); REP(i,0,msk) REP(j,0,msk) if(!(j&k)) { B[i][j|k]+=A[i][j]; } memcpy(A,B,sizeof(A)); from = bdcol[now].b; } printf("Case %d: %d ", kase, A[msk-1][msk-1]);//从-1排满到最后一排满 kase++; } return 0; }