2010-1064
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
题意:
先给出n个ai,通过背包得到一个元素的值都<=L的集合S
然后对于每个bi,在S集中得到它的所有倍数x
对于每个x,每次可以除掉其一个质因子,把其变为1的不同的方案数加到答案中
问最终答案为多少
解法:
首先预处理出每个数将其变为1的不同方案数有多少
假设x=p1^r1^ * p2^r2^ * ... * pn^rn^ (p1 < p2 < ... < pn)
则way[x] = way[x / (p1^r1^)] * C[r1 + r2 + ... + rn][r1]
因此可以通过dp计算上述公式
然后对于每个case,先用n个ai做一次背包
注意相同的bi需要重复计算
对于bi,直接枚举x = bi, 2 * bi, 3 * bi, ... 找出所有在S集中出现的x
然后res += way[x]即可
代码:
{{{
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 20 + 1;
const int MAXM = 100000 + 1;
const int MAXL = 1000000 + 1;
int n, m, L, a[MAXN], b[MAXM], C[MAXN][MAXN];
bool S[MAXL];
const int MAXPRIME = 1000000 + 1;
int plist[MAXPRIME], pcount, minfactor[MAXPRIME], minfactorn[MAXPRIME], factorn[MAXPRIME], pfactorn[MAXPRIME], way[MAXPRIME], reduce[MAXPRIME];
bool mark[MAXPRIME];
inline void initPrime() {
mark[0] = mark[1] = true;
factorn[1] = way[1] = reduce[1] = 1;
for (int i = 2; i < MAXPRIME; i++) {
if (mark[i]) continue;
for (int j = i; j < MAXPRIME; j += i) {
if (!minfactor[j]) {
minfactor[j] = i;
}
}
}
for (int i = 2; i < MAXPRIME; i++) {
int j = i / minfactor[i];
pfactorn[i] = pfactorn[j] + 1;
if (minfactor[j] == minfactor[i]) {
reduce[i] = reduce[j];
minfactorn[i] = minfactorn[j] + 1;
factorn[i] = factorn[j] / minfactorn[i] * (minfactorn[i] + 1);
} else {
reduce[i] = j;
minfactorn[i] = 1;
factorn[i] = factorn[j] << 1;
}
way[i] = way[reduce[i]] * C[pfactorn[i]][minfactorn[i]];
}
}
int main() {
C[0][0] = 1;
for (int i = 1; i < MAXN; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j++) {
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
}
initPrime();
int task;
scanf("%d", &task);
for (int oo = 0; oo < task; oo++) {
scanf("%d%d%d", &n, &m, &L);
memset(S, 0, sizeof(bool) * (L + 1));
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
S[a[i]] = true;
}
for (int i = 0; i <= L; i++) {
if (!S[i]) continue;
for (int j = 0; j < n; j++) {
int l = i + a[j];
if (l <= L) S[l] = true;
}
}
for (int i = 0; i < m; i++) {
scanf("%d", &b[i]);
}
sort(b, b + m);
long long sum = 0, pre = 0;
for (int o = 0; o < m; o++) {
if (!o || b[o] != b[o - 1]) {
int oo = b[o];
long long tmpsum = 0;
for (int i = oo, j = 1; i <= L; i += oo, j++) {
if (S[i]) {
tmpsum += way[j];
}
}
pre = tmpsum;
}
sum += pre;
}
printf("%lld\n", sum);
}
return 0;
}
}}}
题意:
先给出n个ai,通过背包得到一个元素的值都<=L的集合S
然后对于每个bi,在S集中得到它的所有倍数x
对于每个x,每次可以除掉其一个质因子,把其变为1的不同的方案数加到答案中
问最终答案为多少
解法:
首先预处理出每个数将其变为1的不同方案数有多少
假设x=p1r1 * p2r2 * ... * pnrn (p1 < p2 < ... < pn)
则way[x] = way[x / (p1r1)] * C[r1 + r2 + ... + rn][r1]
因此可以通过dp计算上述公式
然后对于每个case,先用n个ai做一次背包
注意相同的bi需要重复计算
对于bi,直接枚举x = bi, 2 * bi, 3 * bi, ... 找出所有在S集中出现的x
然后res += way[x]即可
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 20 + 1;
const int MAXM = 100000 + 1;
const int MAXL = 1000000 + 1;
int n, m, L, a[MAXN], b[MAXM], C[MAXN][MAXN];
bool S[MAXL];
const int MAXPRIME = 1000000 + 1;
int plist[MAXPRIME], pcount, minfactor[MAXPRIME], minfactorn[MAXPRIME], factorn[MAXPRIME], pfactorn[MAXPRIME], way[MAXPRIME], reduce[MAXPRIME];
bool mark[MAXPRIME];
inline void initPrime() {
mark[0] = mark[1] = true;
factorn[1] = way[1] = reduce[1] = 1;
for (int i = 2; i < MAXPRIME; i++) {
if (mark[i]) continue;
for (int j = i; j < MAXPRIME; j += i) {
if (!minfactor[j]) {
minfactor[j] = i;
}
}
}
for (int i = 2; i < MAXPRIME; i++) {
int j = i / minfactor[i];
pfactorn[i] = pfactorn[j] + 1;
if (minfactor[j] == minfactor[i]) {
reduce[i] = reduce[j];
minfactorn[i] = minfactorn[j] + 1;
factorn[i] = factorn[j] / minfactorn[i] * (minfactorn[i] + 1);
} else {
reduce[i] = j;
minfactorn[i] = 1;
factorn[i] = factorn[j] << 1;
}
way[i] = way[reduce[i]] * C[pfactorn[i]][minfactorn[i]];
}
}
int main() {
C[0][0] = 1;
for (int i = 1; i < MAXN; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j++) {
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
}
initPrime();
int task;
scanf("%d", &task);
for (int oo = 0; oo < task; oo++) {
scanf("%d%d%d", &n, &m, &L);
memset(S, 0, sizeof(bool) * (L + 1));
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
S[a[i]] = true;
}
for (int i = 0; i <= L; i++) {
if (!S[i]) continue;
for (int j = 0; j < n; j++) {
int l = i + a[j];
if (l <= L) S[l] = true;
}
}
for (int i = 0; i < m; i++) {
scanf("%d", &b[i]);
}
sort(b, b + m);
long long sum = 0, pre = 0;
for (int o = 0; o < m; o++) {
if (!o || b[o] != b[o - 1]) {
int oo = b[o];
long long tmpsum = 0;
for (int i = oo, j = 1; i <= L; i += oo, j++) {
if (S[i]) {
tmpsum += way[j];
}
}
pre = tmpsum;
}
sum += pre;
}
printf("%lld\n", sum);
}
return 0;
}