首先,对于每一个素数x,f (x) = 2。对于 x 含有一个大于等于三次的素因子,f (x) = 0。
然后是线性筛的过程,对于i * prime [ j ] 有几种情况:
如果 i % prime[ j ] == 0 :
如果 i % ( prime[ j ] * prime[ j ] ) == 0 那么,无论把素因子放到那一边,另一边肯定有素因子的平方或三次方,所以这种情况下 f [ i * prime[ j ] ] = 0
如果不为0,那么只能把prime [ j ] 各放一边,所以 f [ i * prime[ j ] ] = f [i / prime[ j ]]
如果不为0 那么prime[j] 可以放在两边,所以f [ i * prime[ j ] ] = 2*f[ i ]
然后求前缀和即可
这个题不算难,但是比赛的时候一点头绪都没有。线性筛都不熟悉,自己还是太菜了,要补的知识点太多。。。
代码:
// zyc 2018/9/3
// ICPC网络赛J 线性筛 + 前缀和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 7;
const int M = 2e7 + 7;
#define rep(i, a, b) for(int i = a; i < b; i ++)
#define per(i, a, b) for(int i = a; i > b; i --)
bool notprime [M];
int prime [N],ans [M];
void init ()
{
int cnt = 0; notprime[1] = 1; ans[1] = 1;
rep (i, 2, M) {
if (! notprime[i]) {
prime[cnt ++] = i; ans[i] = 2;
}
for (int j = 0; j < cnt && i * prime[j] < M; j ++) {
notprime[i * prime[j]] = 1;
if (i % prime[j] == 0) {
if ((i / prime[j] % prime[j]) == 0) {
ans[i * prime[j]] = 0;
} else {
ans[i * prime[j]] = ans[i / prime[j]];
}
break;
}
ans[i * prime[j]] = 2 * ans[i];
}
}
rep (i, 2, M) {
ans [i] += ans[i - 1];
}
}
int main ()
{
init();
int t; scanf ("%d", &t);
while (t --) {
int n; scanf ("%d", &n);
printf ("%d\n", ans[n]);
}
return 0;
}