【题目链接】
https://loj.ac/problem/6053
【题解】
min_25筛的模板题。
min_25筛可以解决以下一类积性函数的求和问题。
1.
可以通过多项式表达出来。
2.
可以快速算出。
这道题中,
^
所以条件1满足,条件2显然也是满足的。
part1
我们先来考虑一个问题,如何求
,T是N通过除法能得到的数,大约有
种取值。
不难发现,
因为只有2异或了后会加1。
那么我们要求的就是质数的和以及质数的个数。
首先筛出
中的所有质数,记作
。
记函数
表示
。
那么相当于求出
考虑从
推到
,就是从中去除最小质因子是
的,于是可以推出:
相当于强制选出一个
,剩下的随便选,但要重新加上有比
更小的质因子的方案。
边界条件是
,可以
计算。
由于我们只要求
个取值,可以将它们一起考虑。
具体来说,我们从前往后枚举每一个质数
由于大的的取值不会影响小的,所以从后往前更新。
现在的复杂度是
考虑优化,当
时,
,因为不可能存在一个有
的因子但不是质数的数,所以每次有一段前缀不用算。
现在的复杂度是
?,我不会证,但肯定比
小。
Part2
处理出了
接下来就简单了。
记
为
,我们要求的是
。
由于我们已经知道了
(质数的答案和)。现在要加入非质数的答案。还是考虑枚举质数:
由于
所以它的
值也是已知的。
边界条件是
时,值为0。
这个相当于是把上一部分的递归实现,所以复杂度与Part1相同。
总时间复杂度:
一般能做到
【代码】(为了便于调试很多地方写的比较烦)
/* - - - - - - - - - - - - - - -
User : VanishD
problem : loj-6053
Points : Min_25
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# 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;
}
}
}
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;
}
}
}
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;
}