[玲珑学院 1031 Bob and Alice are eating food]DP+矩阵快速幂

[玲珑学院 1031 Bob and Alice are eating food]DP+矩阵快速幂

题目链接[玲珑学院 1031 Bob and Alice are eating food]
题意描述:有apples,pears,peaches,bananas, oranges and lemons.六种水果。每种水果数量可以看成无限多。吃K个水果,要保证吃的apples,pears 是偶数(0也算是偶数),求方案数模 1e9+7
解题思路:表示官方题解母函数+泰勒展开看不懂。问了一下凯神和qwb,才明白了DP+矩阵快速幂的做法。主要还是对状态的理解,对K进行状态转移,可以只去考虑apple和pear。
ij(0i,j1) 分别表示apple,pear的奇偶性, 0 表示偶数,1表示奇数,那么 Aij 表示apple,pear满足对应的奇偶性条件下的所有方案数。
那么,就可以用 A00,A01,A10,A11 表示所有的状态。
列出状态转移方程如下:

A00=4A00+A01+A10;A01=4A01+A00+A11;A10=4A10+A01+A11;A11=4A11+A01+A10;

然后,就可以构造初始矩阵和转移矩阵了。


感觉自己往往很多题目都很难想到矩阵快速幂啊。引用一下凯神的心得。

先写方程,如果发现转移的时候是一种线性的转移,那么就可以尝试构造转移矩阵了。比如这个题,你把状态转移方程写出来,发现就是几个状态之间的线性变换,那么就可以写成矩阵的形式了

#include <queue>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

//#pragma comment(linker, "/STACK:1024000000,1024000000")

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define fst             first
#define snd             second

typedef long long LL;
typedef vector<LL> Vec;
typedef vector<Vec> Mat;

const LL MOD = 1e9 + 7;
LL T, K;
Mat mat_mul(Mat a, Mat b) {
    Mat ret(4, Vec(4, 0));
    int ra = a.size(), rb = b.size(), t = a[0].size();
    for (int k = 0; k < t; k++) {
        for (int i = 0; i < ra; i++) {
            for (int j = 0; j < rb; j++) {
                ret[i][j] = (ret[i][j] + a[i][k] * b[k][j]) % MOD;
            }
        }
    }
    return ret;
}
Mat mat_pow(Mat a, LL b) {
    Mat ret(4, Vec(4, 0));
    for (int i = 0; i < ret.size(); i++) {
        ret[i][i] = 1;
    }
    while(b > 0) {
        if(b & 1LL) ret = mat_mul(ret, a);
        a = mat_mul(a, a);
        b >>= 1;
    }
    return ret;
}
void mat_assign(Mat& a, LL ar[][4]) {
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            a[i][j] = ar[i][j];
        }
    }
}
int main() {
#ifndef ONLINE_JUDGE
//    FIN;
#endif // ONLINE_JUDGE
    LL cas = 0;
    LL m1[4][4] = {
        {4, 1, 1, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0}
    };
    LL m2[4][4] = {
        {4, 1, 1, 0},
        {1, 4, 0, 1},
        {1, 0, 4, 1},
        {0, 1, 1, 4}
    };
    Mat init(4, Vec(4));
    Mat tras(4, Vec(4));
    mat_assign(init, m1);
    mat_assign(tras, m2);
    Mat res(4, Vec(4));
    scanf("%lld", &T);
    while(T --) {
        scanf("%lld", &K);
        res = mat_pow(tras, K - 1);
        res = mat_mul(init, res);
        printf("Case #%lld: %lld\n", ++cas, res[0][0]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值