[蓝桥杯 2015 省 AB] 垒骰子

[蓝桥杯 2015 省 AB] 垒骰子

题目描述

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

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

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

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

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

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

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

不要小看了 atm 的骰子数量哦~。

输入格式

第一行两个整数 n n n m m m

n n n 表示骰子数目。

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

输出格式

一行一个数,表示答案模 1 0 9 + 7 10^9+7 109+7 的结果。

样例 #1

样例输入 #1

2 1
1 2

样例输出 #1

544

提示

对于 30 % 30\% 30% 的数据: n ≤ 5 n \le 5 n5

对于 60 % 60\% 60% 的数据: n ≤ 100 n \le 100 n100

对于 100 % 100\% 100% 的数据: 0 < n ≤ 1 0 9 , m ≤ 36 0<n \le 10^9,m \le 36 0<n109,m36

时限 2 秒, 256M

蓝桥杯 2015 年省赛 AB 组 I 题。

思路

本道题先看数据范围,n比较大,那么我们可以先思考当n比较小的时候,本题怎么做:我们可以思考用dp来解决这道题(因为本道题我们可以试着用集合划分(也就是当前状态可以用前一个状态转移过来的)),因此,我们用f[i][j]表示第i个骰子的上面的数字j,再分析,我们可以知道,如下图
在这里插入图片描述
f[i][j]转移状态的系数都是固定的,(这里为什么系数为4?因为每个骰子只能转动4次),对于这样的情况,我们可以利用矩阵的乘法进行加速(类似于斐波那契数列的优化版本),因此我们可以:
在这里插入图片描述
注意:f初始化为6个4,还有就是我们可以把f的矩阵大小跟a的矩阵对齐,这样就不必再写个mul函数了。

代码


//n<=1e9考虑快速幂
//只有当n的范围小的时候用dp更好
//大的话,我们可以考虑矩阵优化版
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;

const int N = 6,mod=1e9+7;

bool st[N][N];
int n,m;

int get_op(int x){
    if(x>=3)return x-3;
    return x+3;
}

void mul(int c[][N],int a[][N],int b[][N]){
    static int temp[N][N];
    
    memset(temp,0,sizeof temp);
    
    for(int i=0;i<6;i++){
        for(int j=0;j<6;j++){
            for(int k=0;k<6;k++){
                temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%mod;
            }
        }
    }
    memcpy(c,temp,sizeof temp);
}

signed main(){
    cin>>n>>m;
    
    while(m--){
        int a,b;
        cin>>a>>b;
        a--,b--;
        st[a][get_op(b)]=true;
        st[b][get_op(a)]=true;
    }
    
    int f[N][N]={4,4,4,4,4,4};
    
    int a[N][N]={0};
    
    for(int i=0;i<6;i++){
        for(int j=0;j<6;j++){
            if(!st[i][j]){
                a[i][j]=4;
            }
        }
    }
    
    n--;
    
    while(n){
        if(n&1)mul(f,f,a);
        mul(a,a,a);
        n>>=1;
    }
    
    int res=0;
    
    for(int i=0;i<6;i++){
        res=(res+f[0][i])%mod;
    }
    cout<<res;
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值