1 条题解
-
0
题目大意
有 个三状态转轮(状态 ),初始全为 。有 种操作模式,第 种模式选择 个转轮转动 次, 个转轮转动 次,其余不动。每次操作随机选择一个模式,并在该模式的所有可能方案中随机选择一个。求进行 次操作后,恰有 个转轮在状态 , 个转轮在状态 的方案数,对 取模。
算法思路
1. 状态表示与转移
状态定义:用 表示有 个转轮在状态 , 个转轮在状态 的方案数。
状态转移:对于每个模式 ,我们需要计算它如何改变当前状态:
- 转动规则:
- 转动 次:, ,
- 转动 次:, ,
转移计算:对于当前状态 ,枚举:
- 在 个转动 次的转轮中,有多少个来自状态
- 在 个转动 次的转轮中,有多少个来自状态
然后计算新的状态 和对应的组合数系数。
2. 矩阵快速幂优化
由于 可达 ,直接模拟不可行。我们将状态转移表示为矩阵形式,使用矩阵快速幂在 时间内完成 次操作。
状态编码:状态 满足 ,总状态数约为 。
矩阵构造:转移矩阵 的 表示从状态 到 的转移系数。
3. 组合数预处理
预处理阶乘和逆元,使用公式计算组合数:
$$\binom{n}{a,b,c} = \frac{n!}{a!b!c!}, \quad a+b+c=n $$关键代码解析
组合数计算
int comb(int n, int a, int b) { if (a < 0 || b < 0 || a + b > n) return 0; return 1LL * fac[n] * inv_fac[a] % MOD * inv_fac[b] % MOD * inv_fac[n - a - b] % MOD; }
状态转移计算
核心部分是六重循环,枚举所有可能的转移情况:
for (int x0 = 0; x0 <= min(a, n - i - j); x0++) { for (int x1 = 0; x1 <= min(a - x0, i); x1++) { int x2 = a - x0 - x1; // ... 类似枚举 y0, y1, y2 // 计算新状态和组合数系数 } }
矩阵快速幂
void matrix_pow(long long exp) { memset(res, 0, sizeof(res)); res[0][0] = 1; // 初始状态:全0 while (exp) { if (exp & 1) matrix_mult(res, dp, res); matrix_mult(dp, dp, dp); exp >>= 1; } }
复杂度分析
- 状态数:
- 单次转移:(经过优化剪枝)
- 总复杂度:
对于 ,在优化后可以通过大部分测试点。
优化技巧
- 状态剪枝:利用 减少状态枚举
- 组合数预处理: 计算组合数
- 矩阵稀疏性:转移矩阵相对稀疏,可进一步优化
- 模运算优化:使用
long long
避免中间结果溢出
总结
本题是典型的组合计数 + 矩阵快速幂问题,主要难点在于:
- 正确建模状态转移
- 处理大规模指数
- 优化高维状态转移的计算
通过将操作视为线性变换,并用矩阵快速幂加速,我们成功在 时间内解决了 极大的问题。
- 转动规则:
- 1
信息
- ID
- 3314
- 时间
- 1500ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者