P1771 方程的解(隔板法)、P1450 [HAOI2008] 硬币购物(容斥定理、集合的交)、P1044 [NOIP2003 普及组] 栈(卡特兰数)

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;
}

代码思路

  1. 首先定义了一些全局变量:P 表示一个固定的数值 1000 。C 数组用于存储计算过程中的中间结果。len 表示 C 数组中有效数字的长度。

  2. qpow 函数:这是一个快速幂函数,用于计算 a 的 b 次幂对 P 取模的结果。通过位运算和循环来高效地计算幂次。

  3. getC 函数:用于计算组合数。首先将 C[1] 初始化为 1 。第一个循环通过累乘的方式计算 n *... * (n - m + 1) ,并处理每一位的乘法结果可能产生的进位。第二个循环通过逐位除法的方式除以 2 *... * (n - m) ,并处理每一位的除法结果和可能的借位。

  4. 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;
}

代码思路

  1. 定义了一些变量和数组:c[4] 用于存储 4 种物品的价值。d[4] 用于存储每种物品的数量上限。n 表示测试用例的数量。s 表示目标总和。f[100005] 用于完全背包预处理的结果。

  2. pack_pre 函数:这是完全背包的预处理部分。初始设置 f[0] 为 1 。然后通过两层循环,对于每种物品,从其价值开始,逐步累加计算能够达到的总和的组合数。

  3. calc 函数:这是通过容斥原理计算满足条件的组合数的函数。外层循环枚举 1 << 4 种状态,每种状态表示选择某些物品的组合。内层循环计算当前状态下所选物品的总价值 t ,并根据选择的物品数量改变符号 sign 。如果目标总和 s 大于等于当前状态下的总价值 t ,则将相应的组合数累加到结果 res 中。

  4. 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;
}

代码思路

  1. 首先,定义了两个变量:n 用于接收用户输入,表示要计算的项数。f[20] 是一个长整型数组,用于存储计算过程中的中间结果。

  2. 在 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] 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱姌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值