ACM-ICPC 2018 南京赛区网络预赛 Sum

题意不复述
对数x进行质因数分解,

f(x)的求法

x = i = 1 k j = 1 c i p i , j i x=\prod_{i=1}^{k}\prod_{j=1}^{c_i}{p_{i,j}^i} x=i=1kj=1cipi,ji
k 3 f ( x ) = 0 k \leq 3 \Rightarrow f(x)=0 k3f(x)=0
k 2 f ( x ) = 2 c 1 k \leq 2 \Rightarrow f(x)=2^{c_1} k2f(x)=2c1

说人话就是如果分解质因数后:
如果有一个质因子 p p p的指数大于等于3,鸽笼原理知 a , b a,b a,b必然有一个含有两个 p p p,那么 x x x肯定无法分解成两个square-free integer a , b a,b a,b.
如果有一个质因子 p p p的指数等于2,鸽笼原理知 a , b a,b a,b必然有一个含有两个 p p p,那么没得选, a , b a,b a,b各分一个 p p p.
如果有一个质因子 p p p的指数等于1,那么 p p p不是给 a a a就是给 b b b.

指数为1的质因子个数是 c 1 c_1 c1,故总方案数
f ( x ) = 2 c 1 f(x)=2^{c_1} f(x)=2c1

f(x)性质

知道了 f ( x ) f(x) f(x)表达式之后,就可以推导出 f ( x ) f(x) f(x)的性质了。
约定 p p p表示质数

  1. p x f ( p x ) = 2 f ( x ) p \nmid {x} \Rightarrow f(px)=2f(x) pxf(px)=2f(x)
  2. p x , p 2 x f ( p x ) = f ( x p ) p \mid {x},p^2 \nmid {x} \Rightarrow f(px)=f(\frac{x}{p}) px,p2xf(px)=f(px)
  3. p 2 x f ( p x ) = 0 p^2 \mid {x} \Rightarrow f(px)=0 p2xf(px)=0
  4. f ( p ) = 2 , f ( 1 ) = 1 f(p)=2,f(1)=1 f(p)=2,f(1)=1

所以使用欧拉筛法的遍历顺序,使得每个f只会被求一次即可。
预处理所有 f f f f f f的前缀和

code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e7+10;
const int max_prime_cnt = 1271356;
bool is_prime[maxn];
int prime[max_prime_cnt];
int prime_cnt;
int f[maxn];
ll sf[maxn];
int n;
void get_f(int n) {
    memset(is_prime,1,sizeof(is_prime));
    is_prime[1] = false;
    f[1] = 1;
    prime_cnt = 0;
    for (int i = 2; i <= n; ++i) {
        if (is_prime[i]) {
            prime[prime_cnt++] = i;
            f[i] = 2;
        }
        int k = n/i;
        for (int j = 0; j < prime_cnt && prime[j] <= k; ++j) {
            is_prime[i*prime[j]] = false;
            if (i%prime[j] == 0) {
                int t = i/prime[j]; // t*prime[j]^2
                f[i*prime[j]] = t%prime[j] ? f[t] : 0;
                break;
            } else {
                f[i*prime[j]] = 2*f[i];
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    n = maxn-5;
    get_f(n);
    sf[0] = 0;
    for (int i = 1; i <= n; ++i)
        sf[i] = sf[i-1]+f[i];
    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        cout<<sf[n]<<endl;
    }
    return 0;
}