牛客小白月赛7 J方格填色 状态压缩 + 矩阵加速

原题链接: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();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值