【题目链接】
https://loj.ac/problem/6053
【题解】
min_25筛的模板题。
min_25筛可以解决以下一类积性函数的求和问题。
1. f ( x ) ( x ∈ P ) f(x)(x \in P) f(x)(x∈P)可以通过多项式表达出来。
2. f ( x k ) ( x ∈ P ) f(x^k)(x \in P) f(xk)(x∈P)可以快速算出。
这道题中, f ( x ) = x f(x) = x f(x)=x^ 1 = x − 1 ( x ∈ P 且 x ≠ 2 ) 1 = x - 1(x \in P 且x \neq 2) 1=x−1(x∈P且x̸=2)所以条件1满足,条件2显然也是满足的。
part1
我们先来考虑一个问题,如何求 F ( T ) = ∑ i = 1 T f ( i ) ( i ∈ P , T ∈ ⌊ N / i ⌋ ) F(T)=\sum_{i=1}^{T}f(i)(i\in P,T\in \left\lfloor N/i\right\rfloor ) F(T)=∑i=1Tf(i)(i∈P,T∈⌊N/i⌋),T是N通过除法能得到的数,大约有 2 N 2 \sqrt{N} 2N种取值。
不难发现, ∑ i = 1 N f ( i ) ( i ∈ P ) = ∑ i = 1 N i − 1 ( i ∈ P ) + 2 \sum_{i=1}^{N}f(i)(i\in P) = \sum_{i=1}^{N}i-1(i\in P) + 2 ∑i=1Nf(i)(i∈P)=∑i=1Ni−1(i∈P)+2 因为只有2异或了后会加1。
那么我们要求的就是质数的和以及质数的个数。
首先筛出 1.. N 1..\sqrt{N} 1..N中的所有质数,记作 p [ 1 ] . . p [ M ] p[1]..p[M] p[1]..p[M]。
记函数 G k ( n , m ) G_{k}(n, m) Gk(n,m)表示 ∑ i = 1 n i k ( i ∈ P 或 i 的 最 小 质 因 子 > p [ m ] ) \sum_{i = 1}^{n}i^k(i \in P或i的最小质因子>p[m]) ∑i=1nik(i∈P或i的最小质因子>p[m])。
那么相当于求出 G 0 ( T , M ) , G 1 ( T , M ) G_{0}(T,M),G_{1}(T,M) G0(T,M),G1(T,M)
考虑从 G k ( n , m − 1 ) G_{k}(n,m-1) Gk(n,m−1)推到 G k ( n , m ) G_{k}(n,m) Gk(n,m),就是从中去除最小质因子是 p [ m ] p[m] p[m]的,于是可以推出:
G k ( n , m ) = G k ( n , m − 1 ) − p [ m ] k ∗ ( G k ( ⌊ n / p [ m ] ⌋ , m − 1 ) − G k ( p [ m ] − 1 , m − 1 ) ) G_{k}(n,m)=G_{k}(n,m-1)-p[m]^k*(G_{k}(\left\lfloor n/p[m]\right\rfloor,m-1)-G_{k}(p[m]-1,m-1)) Gk(n,m)=Gk(n,m−1)−p[m]k∗(Gk(⌊n/p[m]⌋,m−1)−Gk(p[m]−1,m−1))
相当于强制选出一个 p [ m ] p[m] p[m],剩下的随便选,但要重新加上有比 p [ m ] p[m] p[m]更小的质因子的方案。
边界条件是 G k ( n , 0 ) = ∑ i = 1 n i k G_{k}(n,0)=\sum_{i=1}^{n}i^k Gk(n,0)=∑i=1nik,可以 O ( 1 ) O(1) O(1)计算。
由于我们只要求 2 N 2\sqrt{N} 2N个取值,可以将它们一起考虑。
具体来说,我们从前往后枚举每一个质数 p [ 1 ] . . p [ m ] p[1]..p[m] p[1]..p[m]由于大的的取值不会影响小的,所以从后往前更新。
现在的复杂度是 N ∗ ( s q r t N / l n N ) = O ( N / l n N ) \sqrt{N}*(sqrt{N}/ln{\sqrt{N}})=O(N/ln{\sqrt{N}}) N∗(sqrtN/lnN)=O(N/lnN)
考虑优化,当 p [ m ] 2 > n p[m]^2>n p[m]2>n时, G k ( n , m ) = G k ( n , m − 1 ) G_{k}(n,m)=G_{k}(n,m-1) Gk(n,m)=Gk(n,m−1),因为不可能存在一个有 p [ m ] p[m] p[m]的因子但不是质数的数,所以每次有一段前缀不用算。
现在的复杂度是 ∑ T ∑ i = 1 m [ p [ i ] 2 ≤ T ] ≈ O ( N 3 / 4 / l n N ) \sum_{T}\sum_{i=1}^{m}[p[i]^2 \leq T]\approx O(N^{3/4}/ln{N}) ∑T∑i=1m[p[i]2≤T]≈O(N3/4/lnN)?,我不会证,但肯定比 O ( N 3 / 4 ) O(N^{3/4}) O(N3/4)小。
Part2
处理出了 F F F接下来就简单了。
记 S ( n , m ) S(n,m) S(n,m)为 ∑ i = 1 n f ( i ) [ i 的 最 小 质 因 子 ≥ p [ m ] ] \sum_{i=1}^{n}f(i)[i的最小质因子\geq p[m]] ∑i=1nf(i)[i的最小质因子≥p[m]],我们要求的是 S ( N , 1 ) + f ( 1 ) S(N,1)+f(1) S(N,1)+f(1)。
由于我们已经知道了 F F F(质数的答案和)。现在要加入非质数的答案。还是考虑枚举质数:
S ( n , m ) = F ( n ) − F ( p [ m ] − 1 ) + ∑ i = m M ∑ j = 1 p [ i ] j + 1 ≤ N ( f ( p [ i ] j ) ∗ S ( n / p [ i ] j , i + 1 ) + f ( p [ i ] j + 1 ) ) S(n,m)=F(n)-F(p[m]-1)+\sum_{i=m}^{M}\sum_{j=1}^{p[i]^{j+1}\leq N}(f(p[i]^j)*S(n/p[i]^j,i+1)+f(p[i]^{j+1})) S(n,m)=F(n)−F(p[m]−1)+∑i=mM∑j=1p[i]j+1≤N(f(p[i]j)∗S(n/p[i]j,i+1)+f(p[i]j+1))
由于 p [ M ] ≤ N p[M]\leq \sqrt{N} p[M]≤N所以它的 F F F值也是已知的。
边界条件是 n < p [ m ] n<p[m] n<p[m]时,值为0。
这个相当于是把上一部分的递归实现,所以复杂度与Part1相同。
总时间复杂度: O ( N 3 / 4 / l n N ) O(N^{3/4}/ln{N}) O(N3/4/lnN)一般能做到 1 e 10 1e10 1e10
【代码】(为了便于调试很多地方写的比较烦)
/* - - - - - - - - - - - - - - -
User : VanishD
problem : loj-6053
Points : Min_25
- - - - - - - - - - - - - - - */
# include
# define ll long long
# define inf 0x3f3f3f3f
# define P 1000000007
# define N 1001000
using namespace std;
ll use[N], p[N], pre[N], bak[N], pre0[N], pre1[N], bak0[N], bak1[N];
ll sum, nt, cnt;
int pnum;
const ll inv2 = 5e8 + 4;
void get_p(int n){
use[1] = true;
for (int i = 2; i <= n; i++){
if (!use[i]) p[++pnum] = i;
for (ll j = 1; j <= pnum && 1ll * i * p[j] <= n; j++){
use[i * p[j]] = true;
if (i % p[j] == 0) break;
}
}
}
ll F(ll p, int k){
if (k == 0) return 1;
return p ^ k;
}
void get_Pow0(){
for (int i = 1; i <= nt; i++)
pre0[i] = i, bak0[i] = sum / i;
for (int i = 1, prep = 1, bakp = nt; i <= pnum; i++){
while (prep <= nt && p[i] * p[i] > prep) prep++;
while (bakp >= 1 && p[i] * p[i] > sum / bakp) bakp--;
for (int j = 1; j <= bakp; j++){
ll x = sum / j;
ll tmp1 = (x <= nt) ? pre0[x] : bak0[sum / x],
tmp2 = (x / p[i] <= nt) ? pre0[x / p[i]] : bak0[sum / (x / p[i])],
tmp3 = pre0[p[i] - 1];
bak0[j] = (tmp1 - 1 * (tmp2 - tmp3) + P) % P;
}
for (int j = nt; j >= prep; j--){
ll x = j;
ll tmp1 = (x <= nt) ? pre0[x] : bak0[sum / x],
tmp2 = (x / p[i] <= nt) ? pre0[x / p[i]] : bak0[sum / (x / p[i])],
tmp3 = pre0[p[i] - 1];
pre0[j] = (tmp1 - 1 * (tmp2 - tmp3) + P) % P;
}
}
for (int i = 1; i <= nt; i++) pre0[i] -= 1, bak0[i] -= 1;
}
void get_Pow1(){
for (int i = 1; i <= nt; i++){
pre1[i] = 1ll * i * (i + 1) % P * inv2 % P;
ll tmp = (sum / i) % P;
bak1[i] = tmp * (tmp + 1) % P * inv2 % P;
}
for (int i = 1, prep = 1, bakp = nt; i <= pnum; i++){
while (prep <= nt && p[i] * p[i] > prep) prep++;
while (bakp >= 1 && p[i] * p[i] > sum / bakp) bakp--;
for (int j = 1; j <= bakp; j++){
ll x = sum / j;
ll tmp1 = (x <= nt) ? pre1[x] : bak1[sum / x],
tmp2 = (x / p[i] <= nt) ? pre1[x / p[i]] : bak1[sum / (x / p[i])],
tmp3 = pre1[p[i] - 1];
bak1[j] = (tmp1 - p[i] * (tmp2 - tmp3) % P + P) % P;
}
for (int j = nt; j >= prep; j--){
ll x = j;
ll tmp1 = (x <= nt) ? pre1[x] : bak1[sum / x],
tmp2 = (x / p[i] <= nt) ? pre1[x / p[i]] : bak1[sum / (x / p[i])],
tmp3 = pre1[p[i] - 1];
pre1[j] = (tmp1 - p[i] * (tmp2 - tmp3) % P + P) % P;
}
}
for (int i = 1; i <= nt; i++) pre1[i] -= 1, bak1[i] -= 1;
}
ll get_s(ll n, int m){
if (n <= 1 || n < p[m]) return 0;
ll ans = ((n > nt) ? bak[sum / n] : pre[n]) - pre[p[m] - 1];
for (int i = m; i <= pnum && n >= 1ll * p[i] * p[i]; i++)
for (ll j = 1, k = p[i]; k * p[i] <= n; j++, k *= p[i])
ans = (ans + F(p[i], j) * get_s(n / k, i + 1) + F(p[i], j + 1)) % P;
return ans;
}
int main(){
ll n;
scanf("%lld", &n);
sum = n; nt = sqrt(n);
get_p(nt);
get_Pow0(); get_Pow1();
for (int i = 1; i <= nt; i++){
if (i == 1) pre[i] = 0;
else pre[i] = (pre1[i] - pre0[i] + 2 + P) % P;
bak[i] = (bak1[i] - bak0[i] + 2 + P) % P;
}
p[pnum + 1] = nt + 1;
printf("%lld\n", (get_s(n, 1) + 1) % P);
return 0;
}