ZOJ 3256 Tour in the Castle 插头DP 矩阵乘法

题解  

  这题是一道非常好的插头题,与一般的按格转移的题目不同,由于m很大,要矩阵乘法,这题需要你做一个按列转移的插头DP。

  按列转移多少与按格转移不同,但大体上还是基于连通性进行转移。每一列只有右插头是对下一列的转移有影响的,那么我们只需要记录每一列的右插头的连通情况,用最小表示法表示为当前列的状态。在转移的时候,我们只知道上一列的右插头,即本列的左插头的情况,而上插头还需要自己记一个标记。

  那么我们具体分析:

  1、不存在上插头

    1、同时存在左插头和右插头,不需要修改当前插头,直接把上一列的右插头当做当前列的右插头

    2、只在左插头,即从上一列的某一个连通块转移过来,记录连通块。(左下插头)

    3、只在右插头,即此为一个新的连通块,打上标记,表明这是一个新的连通块。(右下插头)

  2、存在上插头

    1、同时存在左插头和右插头,一个格子里有三个插头,非法状态

    2、都不存在左插头和右插头,不需要修改当前插头,即从上往下。

    3、存在左插头

      1、上插头和左插头同属一个连通块,但不在最终状态(没有右插头)的右下角的格子里出现,非法状态

      2、上插头是左下插头,合并连通块,并删除这两个插头(这个合并比较特殊,因为两个都是已知的连通块,具体画图比较清晰)

      3、上插头是右下插头,合并连通块,删掉当前插头

    4、不存在左插头

      1、上插头是左下插头,合并连通块,删除左下插头

      2、上插头是右下插头,合并为新的连通块

  具体情况还是自己动手画图比较清晰。

  然后就到了矩乘的部分。首先考虑构造矩阵,g[i][j] = 1表示i状态能推到j状态,因此我们只需要枚举这些状态,一个一个判转移就可以了。

  初始的情况下,只存在全空的状态和0和N-1有插头的情况,因此答案就是矩阵快速幂后的ans[1][0],即从初始状态推向终止状态。

 

程序

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <string>
  5 #include <algorithm>
  6 
  7 using namespace std;
  8 
  9 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
 10 #define DWN(i, a, b) for (int i = (a), i##_end_ = (b); i >= i##_end_; --i)
 11 #define mset(a, b) memset(a, b, sizeof(a))
 12 typedef long long LL;
 13 const int MAXD = 15, HASH = 419, STATE = 1010, MOD = 7777777;
 14 int n, m, code[MAXD], ch[MAXD];
 15 struct HASHMAP
 16 {
 17     int head[HASH], nxt[STATE], state[STATE], siz;
 18     void clear() { siz = 0, mset(head, -1); }
 19     int push(int x)
 20     {
 21         int pos = x%HASH, i = head[pos];
 22         for (; i != -1; i = nxt[i])
 23             if (state[i] == x) return i;
 24         state[siz] = x;
 25         nxt[siz] = head[pos], head[pos] = siz++;
 26         return siz-1;
 27     }
 28 }hm;
 29 struct Matrix
 30 {
 31     int mat[200][200], D;
 32     Matrix operator * (const Matrix &AI) const
 33     {
 34         Matrix ret; ret.D = D;
 35         REP(i, 0, D)
 36             REP(j, 0, D)
 37             {
 38                 LL sum = 0;
 39                 REP(k, 0, D) sum += (LL)mat[i][k]*AI.mat[k][j];
 40                 ret.mat[i][j] = sum%MOD;
 41             }
 42         return ret;
 43     }
 44 }rc[30], A, B;
 45 
 46 void decode(int x)
 47 {
 48     DWN(i, n, 1) code[i] = x&3, x >>= 2;
 49 }
 50 
 51 int encode()
 52 {
 53     int cnt = 0, ret = 0;
 54     mset(ch, -1), ch[0] = 0;
 55     REP(i, 1, n)
 56     {
 57         if (ch[code[i]] == -1) ch[code[i]] = ++cnt;
 58         ret <<= 2, ret |= ch[code[i]];
 59     }
 60     return ret;
 61 }
 62 
 63 bool check(int st, int nxt)
 64 {
 65     decode(st);
 66     int up = 0, k, cnt = 0;
 67     REP(i, 1, n)
 68     {
 69         if (up == 0)
 70         {
 71             if (!code[i] && !(nxt&(1<<(i-1)))) return false;
 72             if (code[i] && (nxt&(1<<(i-1)))) continue ;
 73             if (code[i]) up = code[i];
 74             else up = -1;
 75             k = i;
 76         }
 77         else
 78         {
 79             if (code[i] && (nxt&(1<<(i-1)))) return false;
 80             if (!code[i] && !(nxt&(1<<(i-1)))) continue ;
 81             if (code[i])
 82             {
 83                 if (up == code[i] && !(nxt&(1<<(i-1))) && (nxt || i != n)) return false;
 84                 if (up != -1)
 85                 {
 86                     REP(j, 1, n) if (code[j] == code[i] && j != i) code[j] = code[k];
 87                     code[i] = code[k] = 0;
 88                 }
 89                 else code[k] = code[i], code[i] = 0;
 90             }
 91             else
 92             {
 93                 if (up != -1) code[i] = code[k], code[k] = 0;
 94                 else code[i] = code[k] = n+(++cnt);
 95             }
 96             up = 0;
 97         }
 98     }
 99     if (up) return false;
100     return true;
101 }
102 
103 void init()
104 {
105     if (rc[n].D != 0)
106     { B = rc[n]; return ; }
107     mset(rc[n].mat, 0);
108     hm.clear(), hm.push(0); 
109     mset(code, 0), code[1] = code[n] = 1, hm.push(encode());
110     decode(hm.state[1]);
111     for (int i = 1; i < hm.siz; ++i)
112         REP(nxt, 0, ((1<<n)-1))
113             if (check(hm.state[i], nxt))
114             {
115                 int j = hm.push(encode());
116                 rc[n].mat[i][j] ++;
117             }
118     rc[n].D = hm.siz-1;
119     B = rc[n];
120 }
121 
122 void work()
123 {
124     mset(A.mat, 0); A.D = B.D;
125     REP(i, 0, A.D) A.mat[i][i] = 1; 
126     while (m > 0)
127     {
128         if (m&1) A = A*B;
129         B = B*B, m >>= 1;
130     }
131     if (!A.mat[1][0]) puts("Impossible");
132     else printf("%d\n", A.mat[1][0]);
133 }
134 
135 int main()
136 {
137     REP(i, 0, 20) rc[i].D = 0;
138     while (~scanf("%d %d", &n, &m))
139         init(), work();
140     return 0;
141 }
View Code

 

posted @ 2017-02-25 11:32  Splay  阅读(330)  评论(0编辑  收藏  举报