1. 问题理解与建模思路
垒骰子问题看似简单,实则蕴含了动态规划和矩阵快速幂的精妙应用。我们先从骰子的基本特性入手:标准骰子中1对4、2对5、3对6互为对面。当我们将n个骰子垂直垒起来时,每个骰子的底面必须与下方骰子的顶面数字相容(即不在排斥列表中)。
关键观察点在于:骰子的每个面朝下时,下一个骰子的底面选择只与当前骰子的顶面数字有关。这构成了一个状态转移关系,可以用6×6的转移矩阵T表示,其中T[i][j]=1表示当底层骰子顶面为i时,上层骰子底面可以为j(不考虑旋转情况)。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 转移矩阵构建细节
2.1 基础转移矩阵
初始状态下,任何两个数字的面都可以相邻,因此转移矩阵全1:
code复制T = [
[1,1,1,1,1,1],
[1,1,1,1,1,1],
[1,1,1,1,1,1],
[1,1,1,1,1,1],
[1,1,1,1,1,1],
[1,1,1,1,1,1]
]
2.2 处理排斥关系
对于每组排斥对(a,b),我们需要禁止两种情况:
- 下层顶面为a时上层不能以b的对面为底面
- 下层顶面为b时上层不能以a的对面为底面
例如排斥对(1,2):
- a=1的对面是4 → 禁止T[1][2的对面]=T[1][5]=0
- b=2的对面是5 → 禁止T[2][1的对面]=T[2][4]=0
3. 矩阵快速幂优化
3.1 为什么需要快速幂
直接动态规划的时间复杂度是O(n),当n≤1e9时完全不可行。观察到状态转移是线性的且固定,可以用矩阵快速幂在O(log n)时间内计算n-1次转移。
3.2 快速幂实现要点
cpp复制mat qpow(mat x, LL y) {
mat ans;
// 初始化单位矩阵
for(int i=1;i<=N;i++) ans.a[i][i]=1;
while(y) {
if(y&1) ans = mul(ans,x);
x = mul(x,x);
y >>=1;
}
return ans;
}
其中mul函数实现矩阵乘法,注意每步都要取模防止溢出。
4. 旋转因素处理
每个骰子在确定底面后,可以通过旋转得
