Typical DP Contest - M 家
M: 家 - Typical DP Contest | AtCoder
- bit dp + 繰り返し二乗法。 bit dp すらまともに書けなかった。orz
template<typename T> using V = vector<T>; template<typename T> using VV = V<V<T>>; // O(N^3) VV<ll> mul(const VV<ll> &a, const VV<ll> &b, const int M) { int n = a.size(); VV<ll> c(n,V<ll>(n,0)); REP(i, n) REP(j, n) REP(k, n) (c[i][j] += a[i][k]*b[k][j]) %= M; return move(c); } // O(N^3*log(k)) VV<ll> mpow(VV<ll> &&a, int k, int M) { int n = a.size(); VV<ll> b(n,V<ll>(n,0)); REP(i, n) b[i][i] = 1; while (k > 0) { if (k & 1) b = mul(b,a,M); a = mul(a,a,M); k >>= 1; } return move(b); } class m_house { public: void solve(void) { const int MOD = (int)(1e+9)+7; ll H; int R; cin>>H>>R; vector<vector<int>> g(R, vector<int>(R,0)); REP(i, R) REP(j, R) cin>>g[i][j]; // // 一つの階を i->j まで移動する方法は bit DP で計算できる // // A[i][j] := i->j まで移動する方法の総数 // // とすると A*A*...*A = A^H が H 階から降りる方法の総数となる。 // A^H を繰り返し二乗法で計算すればよい // VV<ll> A(R, V<ll>(R, 0)); // dp[S][i][j] := i から出発して S の部屋を通過して j へ移動する移動方法の総数 VV<V<ll>> dp((1<<R), VV<ll>(R, V<ll>(R, 0))); REP(i, R) dp[(1<<i)][i][i] = 1; // O(2^R*R^3) <= 268435456 < 10^9 // 8秒が制限なんでなんとか間に合いそう REP(S,(1<<R)) // S が小さいものからやる ( S|(1<<j) を更新対象にするため) REP(i,R) REP(k,R) { // i->k->j と k 経由で j まで到達するときを加算する // S をとおる i->k path がないなら飛ばす if (dp[S][i][k] == 0) continue; REP(j,R) { // 二回同じ部屋kを通過してしまったり、j->k の移動ができないときは飛ばす if ( S&(1<<j) || !g[j][k] ) continue; int S2 = (S | (1<<j)); (dp[S2][i][j] += dp[S][i][k]) %= MOD; } } REP(i, R) REP(j, R) REP(S,(1<<R)) (A[i][j] += dp[S][i][j]) %= MOD; // O(log(H)*R^3) <= 86016 A = mpow(move(A), H, MOD); cout<<A[0][0]<<endl; return; } };