题目链接
6338. 猴子碰撞的方法数
题目描述
现在有一个正凸多边形,其上共有 n 个顶点。顶点按顺时针方向从 0 到 n - 1 依次编号。每个顶点上 正好有一只猴子 。下图中是一个 6 个顶点的凸多边形。
每个猴子同时移动到相邻的顶点。顶点 i 的相邻顶点可以是:
顺时针方向的顶点 (i + 1) % n ,或
逆时针方向的顶点 (i - 1 + n) % n 。
如果移动后至少有两个猴子位于同一顶点,则会发生 碰撞 。
返回猴子至少发生 一次碰撞 的移动方法数。由于答案可能非常大,请返回对 10^9+7 取余后的结果。
题解
显然结果为2^n - 2,但是需要取余,可以用快速幂解决
假设x=3,n=5。那么5的二进制是:101
3^5 = 3^(101) = 3^(2^0) * 3^(2^2) = 3 * 81 = 243
思路如下
/**
* @param {number} n
* @return {number}
*/
var monkeyMove = function(n) {
const MOD = 1e9 + 7
return (power(2, n) - 2 + MOD) % MOD
};
function power(a, b) {
let ans = 1
const MOD = 1e9 + 7
while (b) {
if (b & 1) ans = (ans * a) % MOD
a = (a * a) % MOD
b >>= 1
}
return ans
}
但是当n过大会出错,我估计可能是a * a这里结果过大精度有问题,于是我改用BigInt
/**
* @param {number} n
* @return {number}
*/
var monkeyMove = function (n) {
const MOD = BigInt(1e9 + 7)
var myPow = function (x, n) {
let ans = 1n
// // 如果负数,2^-2可以变成(1/2)^ 2 不过题目中n > 3
// if (n < 0) {
// x = 1 / x; // js中默认不是整除
// n = -n;
// }
while (n > 0n) {
if (n & 1n) ans = ans * x % MOD
x = x * x % MOD
// n = n >> 1n // BigInt中不支持>>> 但是这题>>也能过
n = n / 2n // BigInt / 会忽略小数
}
return ans
};
// +MOD是因为-2后结果可能为负数
return Number((myPow(2n, BigInt(n)) - 2n + MOD) % MOD)
};