HDU1521——排列组合(生成指数函数)、P3455 [POI2007] ZAP-Queries、P3327 [SDOI2015] 约数个数和

20 篇文章 0 订阅

HDU1521——排列组合(生成指数函数)

题目描述

问题 - 1521 (hdu.edu.cn)

运行代码

#include <iostream>
#include <algorithm>
#define N 51000
using namespace std;
double c1[N], c2[N];
double fac[N];
int num[N], money[N];
int main() {
    int n, m;
    fac[0] = fac[1] = 1;
    for (int i = 2; i < N; i++) fac[i] = fac[i - 1] * i;
    while (~scanf_s("%d%d", &n, &m)) {
        int max = 0;
        memset(c1, 0, sizeof(c1));
        memset(c2, 0, sizeof(c2));
        for (int i = 1; i <= n; i++) 
            scanf_s("%d", &num[i]);
        for (int i = 1; i <= n; i++) 
            money[i] = 1;
        c1[0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= max; j++) {
                int limit = num[i];
                for (int k = 0; k <= limit; k++) {
                    c2[j + k * money[i]] += c1[j] / fac[k];
                }
            }
            memcpy(c1, c2, sizeof(c2));
            memset(c2, 0, sizeof(c2));
            max += num[i] * money[i];
        }
        printf("%d\n", (int)(c1[m] * fac[m] + 0.5));
    }
    return 0;
}

代码思路

  • c1 和 c2 :用于存储计算过程中的中间结果。
  • fac :存储阶乘值。
  • num :可能用于存储每个阶段的相关数量。
  • money :可能表示某种价值或权重。

主函数内部逻辑

  1. 首先初始化阶乘数组 fac 。
  2. 在输入 n 和 m 后,通过循环初始化 c1 和 c2 为 0 ,并读取 num 的值,设置 money 。
  3. 将 c1[0] 初始化为 1 。
  4. 然后通过三层嵌套的循环进行计算:最外层循环控制计算的轮数,从第 1 轮到第 n 轮。第二层循环遍历当前已有的结果位置 j ,直到最大值 max 。第三层循环控制当前轮的数量 k ,从 0 到当前轮的上限 num[i] 。在循环中,根据一定的规则更新 c2 的值。
  5. 每一轮计算完成后,将 c2 的值复制到 c1 ,并清空 c2 ,同时更新最大值 max 。
  6. 最后根据计算结果 c1[m] 和阶乘 fac[m] 计算并输出最终结果。

P3455 [POI2007] ZAP-Queries

题目描述

P3455 [POI2007] ZAP-Queries - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

运行代码

#include <iostream>
using namespace std;
const int N = 50005;
int vis[N], p[N], mu[N], cnt;
void init() {
    mu[1] = 1;
    for (int i = 2; i < N; i++) {
        if (!vis[i]) {
            p[++cnt] = i;
            mu[i] = -1;
        }
        for (int j = 1; i * p[j] < N; j++) {
            vis[i * p[j]] = 1;
            if (i % p[j] == 0) {
                break;
            }
            mu[i * p[j]] = -mu[i];
        }
    }
    for (int i = 1; i < N; i++) {
        mu[i] += mu[i - 1];
    }
}
long long calc(int n, int m, int k) {
    if (n > m) {
        swap(n, m);
    }
    n /= k;
    m /= k;
    long long ans = 0;
    for (int l = 1, r; l <= n; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        ans += (long long)(mu[r] - mu[l - 1]) * (n / l) * (m / l);
    }
    return ans;
}
int main() {
    init();
    int T, n, m, k;
    cin >> T;
    while (T--) {
        cin >> n >> m >> k;
        cout << calc(n, m, k) << endl;
    }
    return 0;
}

代码思路

函数 init :初始化一些数组和变量用于后续计算。首先将 mu[1] 设为 1 。然后从 2 到 N - 1 遍历每个数 i :对于每个素数 p[j] ,如果 i * p[j] < N ,标记 vis[i * p[j]] 为已访问。如果 i 能被 p[j] 整除,则跳出内层循环;否则,设置 mu[i * p[j]] = -mu[i] 。如果 i 未被标记为已访问(即未被筛掉),将其标记为素数 p[cnt] ,并设置 mu[i] 为 -1 ,同时增加素数计数器 cnt 。最后通过累加的方式计算前缀和,将 mu[i] 加上 mu[i - 1] 。

函数 calc :用于计算特定的数值。首先处理 n 和 m 的大小关系,确保 n 不大于 m 。然后将 n 和 m 分别除以 k 进行归一化处理。通过两层循环计算结果:外层循环控制左边界 l ,内层循环计算右边界 r 。在每次循环中,根据 mu 的值和一些计算得出部分结果累加到 ans 中。

主函数 main :首先调用 init 函数进行初始化。读入测试用例的数量 T 。在每次测试用例中,读入 n 、 m 、 k ,调用 calc 函数计算结果并输出。

P3327 [SDOI2015] 约数个数和

题目描述

P3327 [SDOI2015] 约数个数和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

运行代码

#include <iostream>
using namespace std;
const int N = 50010;
int vis[N], p[N], mu[N], cnt;
long long F[N];
void init() {
    mu[1] = 1;
    for (int i = 2; i < N; i++) {
        if (!vis[i]) {
            p[++cnt] = i;
            mu[i] = -1;
        }
        for (int j = 1; i * p[j] < N; j++) {
            vis[i * p[j]] = 1;
            if (i % p[j] == 0) {
                break;
            }
            mu[i * p[j]] = -mu[i];
        }
    }
    for (int i = 1; i < N; i++) {
        mu[i] += mu[i - 1];
    }
    for (int i = 1; i < N; i++) {
        for (int l = 1, r; l <= i; l = r + 1) {
            r = i / (i / l);
            F[i] += 1LL * (r - l + 1) * (i / l);
        }
    }
}
long long calc(int n, int m) {
    if (n > m) {
        swap(n, m);
    }
    long long ans = 0;
    for (int l = 1, r; l <= n; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        ans += (mu[r] - mu[l - 1]) * F[n / l] * F[m / l];
    }
    return ans;
}
int main() {
    init();
    int n, m, T;
    cin >> T;
    while (T--) {
        cin >> n >> m;
        cout << calc(n, m) << endl;
    }
    return 0;
}

代码思路

函数 init :首先初始化 mu 数组,将 mu[1] 设为 1。然后通过两层循环筛选出素数,并设置相应的 mu 值。对于不是素数乘积的数,根据其素因子的情况设置 mu 值。计算 mu 数组的前缀和。接着通过两层嵌套循环计算 F 数组,其中 F[i] 的计算是基于一定的数学规律。

函数 calc :处理 n 和 m 的大小关系,确保 n 不大于 m 。通过两层循环计算最终的结果 ans 。内层循环计算左右边界,根据 mu 的差值与 F 数组的值计算中间的累加和。

主函数 main :调用 init 函数进行初始化操作。读入测试用例的数量 T 。在每个测试用例中,读入 n 和 m ,调用 calc 函数计算结果并输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

筱姌

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

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

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

打赏作者

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

抵扣说明:

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

余额充值