HDU1521——排列组合(生成指数函数)
题目描述
运行代码
#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
:可能表示某种价值或权重。
主函数内部逻辑:
- 首先初始化阶乘数组
fac
。 - 在输入
n
和m
后,通过循环初始化c1
和c2
为 0 ,并读取num
的值,设置money
。 - 将
c1[0]
初始化为 1 。 - 然后通过三层嵌套的循环进行计算:最外层循环控制计算的轮数,从第 1 轮到第
n
轮。第二层循环遍历当前已有的结果位置j
,直到最大值max
。第三层循环控制当前轮的数量k
,从 0 到当前轮的上限num[i]
。在循环中,根据一定的规则更新c2
的值。 - 每一轮计算完成后,将
c2
的值复制到c1
,并清空c2
,同时更新最大值max
。 - 最后根据计算结果
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
函数计算结果并输出。