HDU 6185 Covering

矩阵快速幂。

一开始的思路是$dfs$出一个矩阵,$k[i][j]$表示这一行是状态$i$,将这一行填满,下一行是$j$状态的方案数。然后就可以矩阵快速幂了,但是矩阵大小是$16*16$的,超时了......

仔细观察后会发现,第$0$行状态为$0$,因此往后填充的过程中,并不会出现16种情况,只会在6种状态之间转移:$0000$,$1111$,$1100$,$0011$,$0110$,$1001$。那么只要构造$6*6$的矩阵就可以了。

#include<bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;
int k[6][6];
long long n;
map<long long ,int> ans;
struct M {
  int r;
  int c;
  int a[6][6];
};

M mul(const M &a, const M &b) {
  M res;
  res.r = a.r;
  res.c = b.c;
  memset(res.a, 0, sizeof res.a);
  for(int j = 0; j < res.c; j ++) {
    for(int k = 0; k < a.c ; k ++) {
      if(b.a[k][j] == 0) continue;
      for(int i = 0; i < res.r; i ++) {
        long long u = (long long)a.a[i][k] * (long long)b.a[k][j] % (long long)mod;
        res.a[i][j] = (res.a[i][j] + (int)u) % mod;
      }
    }
  }
  return res;
}

void init() {
  memset(k, 0, sizeof k);
  /*
  0 | 0000
  1 | 1111
  2 | 1100
  3 | 0011
  4 | 0110
  5 | 1001
  */
  k[0][0] ++;
  k[0][1] ++;
  k[0][2] ++;
  k[0][3] ++;
  k[0][5] ++;

  k[1][0] ++;

  k[2][0] ++;
  k[2][3] ++;

  k[3][0] ++;
  k[3][2] ++;

  k[4][5] ++;

  k[5][4] ++;
  k[5][0] ++;
}

void work(long long p) {
  M a;
  memset(a.a, 0, sizeof a.a);
  a.r = 6;
  a.c = 6;
  for(int i = 0; i < 6; i ++) {
    a.a[i][i] = 1;
  }

  M b;
  b.r = 6;
  b.c = 6;
  for(int i = 0; i < 6; i ++) {
    for(int j = 0; j < 6; j ++) {
      b.a[i][j] = k[i][j];
    }
  }

  M c;
  memset(c.a, 0, sizeof c.a);
  c.r = 1;
  c.c = 6;
  c.a[0][0] = 1;

  while(p) {
    if(p & 1) {
      p --;
      a = mul(a, b);
    }
    p = p >> 1;
    b = mul(b, b);
  }

  c = mul(c, a);
  ans[p] = c.a[0][0];
  printf("%d\n", c.a[0][0]);
}

int main() {
  init();
  while(~scanf("%lld", &n)) {
    work(n);
  }
  return 0;
}

  

posted @ 2017-09-08 09:25  Fighting_Heart  阅读(155)  评论(0编辑  收藏  举报