CodeForces 300E Empire Strikes Back
题目描述:
求最小正整数 N ,使得 N!∣∏Ki=1ai! 。
题解:
记 sum=∏Ki=1ai! 。
首先可以想到,对于每一个质数 pi ,找到最小的 Ni 使得 Ni! 中质因子 pi 的出现次数大于等于 sum 里的。
这个玩意儿显然可以二分,于是问题转变成如何求一个数的阶乘里有多少个 pi 因子,答案是 ∑⌊Nipki⌋ ,这部分的复杂度是 O(log2K) 。
再考虑如何求 sum 里质因子 pi 的个数。
令 numx 表示 sum 中因子 x 的出现次数。
如果不考虑阶乘里面的合数分解出来的因子的话,可以简单的求一下后缀和(详见代码),而之后对于一个合数 k ,我们可以用 numk 去更新 numprk 和 numkprk 的值,其中 prk 表示 k 的最质数因子。这样的话每次总是用大数去更新小数,因此从大到小递推即可。
总复杂度 O(log sum log2K) ,大概。
代码:
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;
#define MAXN 1000003
#define MAXV 10000003
static int N, pr_tot, np[MAXV];
static long long sum, mx, tmp, ans = 1, prime[MAXN], pr[MAXV], num[MAXV];
int main()
{
for (long long i = 2; i < MAXV; i++)
{
if (!np[i]) pr[i] = prime[pr_tot++] = i;
for (int j = 0; j < pr_tot && i * prime[j] < MAXV; j++)
{
np[i * prime[j]] = 1;
pr[i * prime[j]] = prime[j];
if (i % prime[j] == 0) break;
}
}
scanf("%d", &N);
for (int i = 0; i < N; i++)
scanf("%lld", &tmp), sum += tmp, mx = max(mx, tmp), num[tmp]++;
for (int i = mx; i > 1; i--) num[i-1] += num[i];
for (int i = mx; i > 1; i--)
if (np[i])
num[pr[i]] += num[i], num[i / pr[i]] += num[i];
for (int i = 0; i < pr_tot && num[prime[i]]; i++)
{
long long l = 1, r = sum, k;
while (l <= r)
{
long long mid = (l + r) >> 1, tot = 0;
for (tmp = mid; tmp; tmp /= prime[i])
tot += tmp / prime[i];
if (tot < num[prime[i]]) l = mid + 1; else r = (k = mid) - 1;
}
ans = max(ans, k);
}
printf("%lld\n", ans);
return 0;
}
提交记录(AC / Total = 1 / 4):
Run ID | Remote Run ID | Time(ms) | Memory(kb) | Result | Submit Time |
---|---|---|---|---|---|
8499606 | 25658399 | 466 | 131000 | WA | 2017-03-20 11:08:20 |
8499611 | 15658414 | 404 | 131000 | WA | 2017-03-20 11:10:17 |
8499647 | 25658464 | 560 | 134912 | RE | 2017-03-20 11:16:09 |
8499658 | 25658482 | 2650 | 205372 | AC | 2017-03-20 11:19:05 |