原题链接:https://ac.nowcoder.com/acm/contest/190/J
题意
有一个m * n的矩阵,如果左右两格不能同为白色,左右两列不能同为黑色,问有多少染色的方法
分析
因为m只有5,而n有1e18因此肯定是用到状态压缩的,很容易我们可以选出相邻两列中可行的状态转移规律,只要两列不同为0 or 与运算后为0,这样都是符合要求的状态,接下来就是用两列来写出递推的公式。
可以构造出一个矩阵来优化线性的计算,我们将所有的状态看成点,这个矩阵其实就是一张图,如果是1,代表i可以到j。接下来就用矩阵自乘来代表列之间的转移,例如i到j的方案数其实就是i到k1,k1到j的方案数+i到k2,k2到j的方案数+…其实就是模拟矩阵乘法的过程
最后先初始化答案矩阵,因为第一列的所有状态都是满足要求的,所以答案矩阵第一列全都置为1
Code
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define re register
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e6 + 10, M = 1e6 + 5, INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int tot;
struct Matrix {
ll a[100][100];
inline Matrix operator * (const Matrix& rhs) {
Matrix ret;
memset(&ret, 0, sizeof ret);
for (int i = 0; i < tot; i++)
for (int j = 0; j < tot; j++)
for (int k = 0; k < tot; k++)
ret.a[i][j] = (ret.a[i][j] + a[i][k] * rhs.a[k][j] % MOD) % MOD;
return ret;
}
}mp;
Matrix ksm(Matrix &a, ll k) {
Matrix ans;
memset(&ans, 0, sizeof ans);
for (int i = 0; i < tot; i++) ans.a[i][i] = 1;
while (k) {
if (k & 1) ans = ans * a;
a = a * a;
k >>= 1;
}
return ans;
}
void solve() {
ll n, m; cin >> m >> n;
tot = 1 << m;
Matrix base;
for (int i = 0; i < tot; i++) {
for (int j = 0; j < tot; j++) {
if (i == 0 && j == 0) continue;
if (!(i & j)) base.a[i][j] = 1;
}
}
base = ksm(base, n-1);
Matrix ans;
for (int i = 0; i < tot; i++) ans.a[0][i] = 1;
ll res = 0;
ans = ans * base;
for (int i = 0; i < tot; i++) res = (res + ans.a[0][i]) % MOD;
cout << res << endl;
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}