斐波那契前 n 项和 || 垒骰子(矩阵乘法)

1303. 斐波那契前 n 项和

斐波那契前n项和
附赠y总保姆级分析
在这里插入图片描述
样例输入:

5 1000

样例输出:

12

代码:

//矩阵乘法d... ... 这他妈是人能想出来的?
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long LL;
const int N = 3;

int n, m;

//矩阵乘法
void mul(int a[], int b[], int c[][N])
{
    int tmp[N] = {0};
    for(int i = 0; i < N; i ++ )
        for(int j = 0; j < N; j ++ )
        {
            tmp[i] = (tmp[i] + (LL)b[j] * (LL)c[j][i]) % m;
        }
        
    memcpy(a, tmp, sizeof tmp);
    return;
}

void mul(int a[][N], int b[][N], int c[][N])
{
    int tmp[N][N] = {0};
    for(int i = 0; i < N; i ++ )
        for(int j = 0; j < N; j ++ )
            for(int k = 0; k < N; k ++ )
            {
                tmp[i][j] = (tmp[i][j] + (LL)a[i][k] * (LL)c[k][j]) % m;
            }
            
    memcpy(a, tmp, sizeof tmp);
    
    return;
}

int main()
{
    cin >> n >> m;
    int f[N] = {1, 1, 1};  //fn fn+1 Sn
    int a[N][N] = {
        {0, 1, 0},
        {1, 1, 1},
        {0, 0, 1},
    };
    
    n --;
    while(n)  //快速幂
    {
        if(n & 1) mul(f, f, a);
        mul(a, a, a);
        n >>= 1;
    }
    
    cout << f[2] << endl;
    
    return 0;
}

 

1217. 垒骰子

垒骰子

赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。

经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!

我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。

假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。

atm想计算一下有多少种不同的可能的垒骰子方式。

两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。

由于方案数可能过多,请输出模 109+7 的结果。

输入格式
第一行包含两个整数 n,m,分别表示骰子的数目和排斥的组数。

接下来 m 行,每行两个整数 a,b,表示 a 和 b 数字不能紧贴在一起。

输出格式
共一个数,表示答案模 109+7 的结果。

数据范围

\(1≤n≤1e9\)
\(1≤m≤36\)
\(1≤a,b≤6\)

输入样例

2 1
1 2

输出样例

544

dp(TLE)

c++代码(只能过7个数据O(6 * 6 * n))

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

typedef long long LL;
const int N = 6, M = 1e7, mod = 1e9 + 7;
int n, m;
int op[8] = {0, 4, 5, 6, 1, 2, 3};
int st[8][8];
int f[M][10]; //由i个骰子垒成的最上面的数为j的方案数

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= m; i ++ )  //冲突
    {
        int a, b;
        cin >> a >> b;
        st[a][b] = st[b][a] = true;
    }
    
    for(int i = 1; i <= n; i ++ )
    {
        for(int j = 1; j <= N; j ++ )   //枚举要放的骰子最上层的数
        {
            if(i == 1) f[i][j] = 4;
            else
            {
                for(int k = 1; k <= N; k ++ ) //枚举当前垒子的最上层的数
                {
                    if(st[op[j]][k]) continue;  //处理冲突
                    f[i][j] = ((LL)f[i][j] % mod + ((LL)f[i - 1][k] * 4) % mod) % mod;
                }                
            }

        }
    }
    int res = 0;
    for(int i = 1; i <= N; i ++ )
        res = (res + f[n][i]) % mod;
    
    cout << res << endl;
    return 0;
}

dp + 矩阵乘法 + 快速幂

处理冲突可以转换成矩阵乘法
c8aead5b4afbf69d68158a6f7e99fb6.png

递推公式: \(f_{n} = f_{1} \ * A^{n - 1}\)
1.\(A^{n - 1}\) ->快速幂
2.\(f_{1}\) 最底层的骰子每个数有四个方向->初始化为4

c++代码 O(6 * 6 * 6 * logn)

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

typedef long long LL;
const int N = 6, mod = 1e9 + 7, M = 7;
int n, m;
int op[8] = {0, 4, 5, 6, 1, 2, 3};

//a = b * c  矩阵乘法
void mul(int a[][M], int b[][M], int c[][M])
{
    int tmp[M][M] = {0};
    for(int i = 1; i <= N; i ++ )
        for(int j = 1; j <= N; j ++ )
            for(int k = 1; k <= N; k ++ )
            {
                tmp[i][j] = ((LL)tmp[i][j] + (LL)b[i][k] * (LL)c[k][j]) % mod;
            }
            
    memcpy(a, tmp, sizeof tmp);
    return;
}

int main()
{
    cin >> n >> m;
    int a[M][M];
    for (int i = 1; i <= N; i ++ )
        for(int j = 1; j <= N; j ++ )
            a[i][j] = 4;
    
    //mul函数f可写成6*6矩阵
    int f[M][M] = {{},{0, 4, 4, 4, 4, 4, 4}}; //代表顶面的数 f[1]
    //构造A矩阵
    for(int i = 1; i <= m; i ++ )
    {
        int x, y;
        cin >> x >> y;
        a[x][op[y]] = 0, a[y][op[x]] = 0;
    }
    
    int k = n - 1;
    while(k)  //快速幂
    {
        if(k & 1) mul(f, f, a);
        mul(a, a, a);
        k >>= 1;
    }
    
    int res = 0;
    for(int i = 1; i <= N; i ++ )
        res = ((LL)res + f[1][i]) % mod;
    
    cout << res << endl;
    
    return 0;
}


 

posted @ 2022-04-04 19:27  panse·  阅读(181)  评论(0)    收藏  举报