P1771 方程的解(隔板法)
题目描述
G29 隔板法 - 董晓 - 博客园 (cnblogs.com)
运行代码
#include <iostream>
#include <cstdio>
using namespace std;
int P = 1000;
int C[10100], len = 1;
int qpow(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % P;
a = a * a % P;
b >>= 1;
}
return res;
}
void getC(int n, int m) {
C[1] = 1;
for (int i = m + 1; i <= n; i++) { // 求 n*...*(n-m+1)
int carry = 0;
for (int j = 1; j <= len; j++) {
int product = C[j] * i + carry;
C[j] = product % 10;
carry = product / 10;
}
while (carry) { // 处理进位
C[++len] = carry % 10;
carry /= 10;
}
}
for (int i = 2; i <= n - m; i++) { // 除以 2*...*(n-m)
int carry = 0;
for (int j = len; j >= 1; j--) {
carry = carry * 10 + C[j];
C[j] = 0;
if (carry >= i) {
C[j] = carry / i;
carry %= i;
}
}
while (len > 1 && C[len] == 0) len--; // 去除前面的 0
}
}
int main() {
int k, x;
cin >> k >> x;
int n = qpow(x % P, x);
getC(n - 1, k - 1);
for (int i = len; i >= 1; i--)
printf("%d", C[i]);
return 0;
}
代码思路
-
首先定义了一些全局变量:
P
表示一个固定的数值 1000 。C
数组用于存储计算过程中的中间结果。len
表示C
数组中有效数字的长度。 -
qpow
函数:这是一个快速幂函数,用于计算a
的b
次幂对P
取模的结果。通过位运算和循环来高效地计算幂次。 -
getC
函数:用于计算组合数。首先将C[1]
初始化为 1 。第一个循环通过累乘的方式计算n *... * (n - m + 1)
,并处理每一位的乘法结果可能产生的进位。第二个循环通过逐位除法的方式除以2 *... * (n - m)
,并处理每一位的除法结果和可能的借位。 -
main
函数:读取输入的k
和x
。计算n = qpow(x % P, x)
。调用getC(n - 1, k - 1)
计算组合数。最后输出组合数的结果,从C
数组的最后一位开始。
P1450 [HAOI2008] 硬币购物(容斥定理、集合的交)
题目描述
P1450 [HAOI2008] 硬币购物 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 100005;
int c[4], d[4], n, s;
LL f[MAXN];
void pack_pre() { // 完全背包预处理
f[0] = 1;
for (int i = 0; i < 4; i++) {
for (int j = c[i]; j < MAXN; j++) {
f[j] += f[j - c[i]];
}
}
}
LL calc(LL s) { // 容斥原理
LL res = 0;
for (int state = 1; state < (1 << 4); state++) { // 枚举状态
LL t = 0, sign = -1;
for (int j = 0; j < 4; j++) { // 过滤状态
if (state & (1 << j)) {
t += c[j] * (d[j] + 1);
sign *= -1;
}
}
if (s >= t) res += f[s - t] * sign;
}
return f[s] - res;
}
int main() {
for (int i = 0; i < 4; i++) scanf("%d", &c[i]);
pack_pre();
scanf("%d", &n);
while (n--) {
for (int i = 0; i < 4; i++) scanf("%d", &d[i]);
scanf("%d", &s);
printf("%lld\n", calc(s));
}
return 0;
}
代码思路
-
定义了一些变量和数组:
c[4]
用于存储 4 种物品的价值。d[4]
用于存储每种物品的数量上限。n
表示测试用例的数量。s
表示目标总和。f[100005]
用于完全背包预处理的结果。 -
pack_pre
函数:这是完全背包的预处理部分。初始设置f[0]
为 1 。然后通过两层循环,对于每种物品,从其价值开始,逐步累加计算能够达到的总和的组合数。 -
calc
函数:这是通过容斥原理计算满足条件的组合数的函数。外层循环枚举1 << 4
种状态,每种状态表示选择某些物品的组合。内层循环计算当前状态下所选物品的总价值t
,并根据选择的物品数量改变符号sign
。如果目标总和s
大于等于当前状态下的总价值t
,则将相应的组合数累加到结果res
中。 -
main
函数:首先读取 4 种物品的价值。调用pack_pre
进行预处理。读取测试用例的数量n
。在每次测试用例中,读取每种物品的数量上限和目标总和s
,然后调用calc
函数计算并输出满足条件的组合数。
P1044 [NOIP2003 普及组] 栈(卡特兰数)
题目描述
P1044 [NOIP2003 普及组] 栈 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
using namespace std;
int n;
long long f[20];
int main(){
cin >> n;
f[0] = 1;
long long prev = f[0];
for(int i = 1; i <= n; i++){
f[i] = prev * (4 * i - 2) / (i + 1);
prev = f[i];
}
cout << f[n] << endl;
return 0;
}
代码思路
-
首先,定义了两个变量:
n
用于接收用户输入,表示要计算的项数。f[20]
是一个长整型数组,用于存储计算过程中的中间结果。 -
在
main
函数中:首先从用户处获取n
的值。初始化f[0]
为 1 。然后通过一个循环从第 1 项计算到第n
项。在每次循环中,根据前一项的值f[i - 1]
,按照特定的公式f[i] = f[i - 1] * (4 * i - 2) / (i + 1)
计算当前项的值f[i]
。最终,输出第n
项的值f[n]
。