您的位置:首页 > 其它

【题解】线性筛素数 线性统计范围质因数 小Y的智力游戏

2017-07-22 17:40 246 查看
对于思维有一定锻炼

关于“线性统计范围质因数”是指统计1到n范围每个数的质因数的总幂次(也可以理解为统计n!的每个质因数的幂次)

例:n = 10时

分解质因数分别有

(1),2,3,22,5,2∗3,7,23,32,2∗5(1),2,3,22,5,2∗3,7,23,32,2∗5

那么结果应为

8,3,2,18,3,2,1

表示2的总幂次是8,3的总幂次是3,5的总幂次为2,7的总幂次为1

题目

题目描述

小 Y 最近迷上了一款智力游戏。(当然小 Y 还是很好学的)

这款智力游戏就是让你从 1 到 N 这 N 个数中,选取若干个不相等的正整数,使得它们的乘积为一个

完全平方数,同时,这个乘积也将成为你的得分。

他(或她,以后这个括号就省略了)想知道最大得分对 1000000007 的模值。

输入

输入文件名 game.in

一行一个正整数 N,代表数字的个数。

输出

输出文件名 game.out

一行一个正整数,代表最大得分对 1000000007 的模值。

输入输出样例

输入输出样例】

Game.inGame.out
31

数据范围

对于 30%的数据,保证有 n≤50。

对于 60%的数据,保证有 n≤1000。

对于 70%的数据,保证有 n≤10000。

对于 80%的数据,保证有 n≤100000。

对于 90%的数据,保证有 n≤1000000。

对于 100%的数据,保证有 n≤3000000。

题解

可以想到,将结果质因数分解后每个质因数的幂次一定是偶数才能是完全平方数

那么就转换为求出答案的每个质因数幂次最多可以是多少

为保证一定是由1到n中不相等的数乘来,直接从n!的质因数入手即可

根据数据范围现在就是要寻求快速统计n!质因数幂次的方法

有两种方法,按时空复杂度优秀度升序分别阐述

方法一

首先要统计质因数幂次肯定怎样都要先把质数求出来,只谈复杂度Onlogn和On的求法都可以,但是可以利用On的线性求法使统计幂次更快求出

在线性筛中,是通过保证每个数都只被最小的质因数计算一遍来实现线性复杂度,现在我们要统计幂次,也可以在线性筛时一起保存每个数的最小质因数,这样在统计时就可以将n不断缩小。具体来说:每次除以当前最小质因数,并将此质因数幂次统计加1

每次至少除以2,复杂度为nlogn

(将求解问题转化为另一个同样的范围更小的问题)

方法二

180204 update

这是“勒让德定理

虽然方法一对于本问题已经完全足够,但是还能更优!

由txc同学本次考试独立发现(ysp也是同样的方法)

感谢txc的解释

方法一需要开大小为n的int数组在线性筛时来存,方法二可根据n直接求出

txc是以质因数含2为例将2, 4, 6, 8…列出,求出每个数含2的幂次,观察可发现规律

不过我观察代码,觉得还可以有这种解释

对于每个质因数,可每一次都对每个数都试除,每次加上有此质因数的个数,直至不能再除即可

当然具体操作不是直接这么操作,其实每次拿n除以当前质因数向下取整所得就是由此质因数的个数。为方便操作,不让n变,逆向,拿n除以当前质数i的次幂,直至当前质数i次幂大于n为止

以n = 18,当前质数p = 2为例

2, 4, 6, 8, 10…含二幂次为

1, 2, 1, 3, 1, 2, 1, 4, 1

i = 1时

0, 1, 0, 2, 0, 1, 0, 3, 0

i = 2时

0, 0, 0, 1, 0, 0, 0, 2, 0



每次变化的个数就是

n除以当前质因数向下取整所得

大概算时间复杂度也是nlogn,但测试速度小于方法一

空间复杂度也更小

代码

#include <cstdio>

typedef long long ll;
const int maxr = 216820, maxn = 3e6 + 100, mod = 1000000007;
bool np[maxn];
int pri[maxr], tot = 0, cnt[maxn];
ll QPow(ll x, int n){
n -= n & 1;
ll res = 1;
while (n){
if (n & 1) res = res * x % mod;
x = x * x % mod; n >>= 1;
}return res;
}
int main (){
int n; scanf ("%d", &n);
for (int i = 2; i <= n; ++i){
if (!np[i])pri[++tot] = i;
for (int j = 1, t = pri[1] * i; j <= tot && t <= n; t = pri[++j] * i){
np[t] = 1;
if (i % pri[j] == 0) break;
}
}
ll sum;
for (int i = 1; i <= tot; ++i){
sum = 1;
while (sum <= n){
sum *= pri[i];
cnt[i] += n / sum;
}
}
ll ans = 1;
for (int i = 1; i <= tot; ++i) ans = ans * QPow(pri[i], cnt[i]) % mod;
printf ("%lld", ans);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: