困难数学题
题目描述
运行代码
#include <iostream>
using namespace std;
int main() {
int x;
cin >> x;
int y = x ^ x ^ x ^ x;
cout << y <<endl;
return 0;
}
代码思路
一、整体思路
- 首先从用户输入中读取一个整数
x
,这个x
的取值范围是0 ≤ x ≤ 500
。 - 然后根据题目要求的运算规则,计算
y = x ^ x ^ x ^ x
。 - 最后将计算得到的结果
y
输出。
二、异或运算(^)原理
异或运算的规则是:对于两个二进制位,如果相同则结果为 0,不同则结果为 1。
例如:
- 0 ^ 0 = 0
- 1 ^ 1 = 0
- 0 ^ 1 = 1
- 1 ^ 0 = 1
三、代码中异或运算的具体分析
在计算 y = x ^ x ^ x ^ x
时:
-
当
x
为一个确定的值时,比如x = 5
(二进制表示为00000101
)。x ^ x
的结果为 0,因为相同的位进行异或运算结果为 0。- 所以
x ^ x ^ x ^ x
就相当于0 ^ x ^ x
。 - 又因为
0 ^ a = a
(任何数与 0 进行异或运算结果为其本身)。 - 所以
0 ^ x ^ x
的结果还是0 ^ x
,即x ^ x ^ x ^ x = x ^ x = 0
。
-
对于任意的
x
,都有相同的结论。因为无论x
的二进制表示是什么,x
与自身进行异或运算结果总是 0,多个x
进行异或运算最终结果也为 0。
综上所述,这段代码通过读取输入的整数 x
,利用异或运算的特性,计算并输出了符合题目要求的结果 y
。
折返跑
题目描述
运行代码
#include <iostream>
typedef long long ll;
const int M = 1e6 + 5;
const ll mod = 1e9 + 7;
ll qpow(ll x, ll ci) {
ll ans = 1;
while (ci > 0) {
if (ci & 1) ans = ans * x % mod;
x = x * x % mod;
ci >>= 1;
}
return ans;
}
int main() {
ll fac[M];
fac[0] = 1;
for (ll i = 1; i < M; i++) fac[i] = fac[i - 1] * i % mod;
int t;
std::cin >> t;
while (t--) {
int n, m;
std::cin >> n >> m;
n -= 2;
m--;
if (n < m) std::cout << "0\n";
else std::cout << fac[n] * qpow(fac[m], mod - 2) % mod * qpow(fac[n - m], mod - 2) % mod << '\n';
}
return 0;
}
代码思路
一、整体思路
- 首先定义了常量
M
和mod
,分别表示一个较大的数和取模的值。 - 定义了快速幂函数
qpow
,用于高效地计算一个数的幂次方对mod
取模的结果。 - 在
main
函数中:- 初始化一个长度为
M
的数组fac
,用于存储阶乘的值,其中fac[0]
初始化为 1,通过循环计算出fac[1]
到fac[M - 1]
的值,每个值都是前一个值乘以当前索引对mod
取模的结果。 - 读取测试数据组数
t
。 - 对于每组测试数据:读取点位数量
n
和折返跑趟数m
。对n
和m
进行处理,将n
减去 2,m
减去 1,以便后续计算。如果处理后的n
小于m
,说明没有合法的推杆方法,输出 0。否则,根据组合数的计算公式,输出fac[n] * qpow(fac[m], mod - 2) % mod * qpow(fac[n - m], mod - 2) % mod
的结果,即从n
个空位中选择m
个位置的组合数对mod
取模的结果。
- 初始化一个长度为
二、原理分析
-
组合数计算:组合数的计算公式为 。在代码中,通过预先计算出阶乘数组
fac
,然后利用公式计算组合数。为了避免除法运算可能导致的精度问题,使用了逆元的方法。对于整数a
和mod
,如果a
和mod
互质,那么a
在模mod
下的逆元inv(a)
满足a * inv(a) ≡ 1 (mod mod)
。在代码中,通过快速幂函数计算fac[m]
和fac[n - m]
在模mod
下的逆元,然后与fac[n]
相乘得到组合数。 -
快速幂与取模:快速幂函数
qpow
使用了位运算的技巧来高效地计算幂次方。通过不断将指数ci
右移一位,同时将底数x
平方,如果当前指数的最低位为 1,则将结果累乘上当前的底数。这样可以在对数时间内计算出幂次方的结果,同时通过不断对中间结果取模,避免了数值过大导致的溢出问题。 -
问题的转化:对于给定的
n
个点位和m
趟折返跑,每次折返都需要推杆,相当于在n - 2
个空位中选择m - 1
个位置来放置右杆的移动位置。这是因为第一次折返前,右杆在位置 2,最后一次折返后,右杆在位置n - 1
,所以有n - 2
个空位可供选择。而选择m - 1
个位置是因为有m - 1
次右杆的移动。根据组合数的定义,这就是从n - 2
个元素中选择m - 1
个元素的组合数,即问题的答案。