[LOJ #6053] 简单的函数

LOJ传送门

题目描述

某一天,你发现了一个神奇的函数 f ( x ) f(x) f(x),它满足很多神奇的性质:

  1. f ( 1 ) = 1 f(1)=1 f(1)=1
  2. f ( p c ) = p ⊕ c f(p^c)=p \oplus c f(pc)=pc p p p 为质数, ⊕ \oplus 表示异或)。
  3. f ( a b ) = f ( a ) f ( b ) f(ab)=f(a)f(b) f(ab)=f(a)f(b) a a a b b b 互质)。

你看到这个函数之后十分高兴,于是就想要求出 ∑ i = 1 n f ( i ) \sum\limits_{i=1}^n f(i) i=1nf(i)

由于这个数比较大,你只需要输出 ∑ i = 1 n f ( i )   m o d   ( 1 0 9 + 7 ) \sum\limits_{i=1}^n f(i) \bmod (10^9+7) i=1nf(i)mod(109+7)

输入输出格式

输入格式

一行一个整数 n n n

输出格式

一行一个整数 ∑ i = 1 n f ( i )   m o d   1000000007 \sum\limits_{i=1}^n f(i) \bmod 1000000007 i=1nf(i)mod1000000007

输入输出样例

样例输入 1
6
样例输出 1
16
样例输入 2
233333
样例输出 2
179004642
样例输入3
9876543210
样例输出3
895670833

数据范围与提示

对于 30 % 30\% 30%的数据, n ≤ 100 n \leq 100 n100
对于 60 % 60\% 60%的数据, n ≤ 1 0 6 n \leq 10^6 n106
对于 100 % 100\% 100%的数据, 1 ≤ n ≤ 1 0 10 1 \leq n \leq 10^{10} 1n1010

解题分析

首先质数除了 2 2 2以外 f ( p ) f(p) f(p)值都是 p − 1 p-1 p1, 所以直接用 m i n 25 min25 min25筛筛出 p r i m e s u m primesum primesum p r i m e c o u n t primecount primecount ,然后特判 1 1 1 2 2 2的情况了。

代码如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ll long long
#define MX 205000
#define MOD 1000000007
#define INV 500000004
int pcnt, dif, sqr;
ll n, val[MX];
int pri[MX], pc[MX], ps[MX], pfix[MX], id1[MX], id2[MX];
//pc for prime counting
//ps for prime sum
bool npr[MX];
void Sieve()
{
	ll tar, buf;
	for (R int i = 2; i <= sqr; ++i)
	{
		if (!npr[i]) pri[++pcnt] = i, pfix[pcnt] = (pfix[pcnt - 1] + i) % MOD;
		for (R int j = 1; j <= pcnt; ++j)
		{
			tar = pri[j] * i;
			if (tar > sqr) break;
			npr[tar] = true;
			if (!(i % pri[j])) break;
		}
	}
	for (R ll lef = 1, rig; lef <= n; lef = rig + 1)
	{
		rig = n / (n / lef);
		val[++dif] = n / lef;
		if (val[dif] <= sqr) id1[val[dif]] = dif;
		else id2[n / val[dif]] = dif;
		pc[dif] = (val[dif] - 1) % MOD;
		ps[dif] = ((val[dif] + 2) % MOD) * ((val[dif] - 1) % MOD) % MOD * INV % MOD;
	}
	for (R int i = 1; i <= pcnt; ++i)
	{
		for (R int j = 1; j <= dif && val[j] >= 1ll * pri[i] * pri[i]; ++j)
		{
			buf = val[j] / pri[i];
			tar = buf <= sqr ? id1[buf] : id2[n / buf];
			pc[j] = (pc[j] - pc[tar] + i - 1) % MOD;
			ps[j] = ((ps[j] - 1ll * pri[i] * (ps[tar] - pfix[i - 1]) % MOD) % MOD + MOD) % MOD;
		}
	}
}
int S(R ll up, R int k)
{
	if (up <= 1 || pri[k] > up) return 0;
	ll tar, buf; int pw;
	tar = up <= sqr ? id1[up] : id2[n / up];
	int ret = ((1ll * ps[tar] - pc[tar] - pfix[k - 1] + k - 1) % MOD + MOD) % MOD;
	for (R int i = k; i <= pcnt && 1ll * pri[i] * pri[i] <= up; ++i)
	{
		buf = pri[i], tar = buf * pri[i];
		for (pw = 1; tar <= up; buf = tar, tar = tar * pri[i], ++pw)
		(ret += (1ll * S(up / buf, i + 1) * (pri[i] ^ pw) % MOD + (pri[i] ^ (pw + 1)) % MOD) % MOD) %= MOD;
	}
	return k == 1 ? (ret + 2) % MOD : ret;
}
int main(void)
{
	scanf("%lld", &n);
	sqr = std::sqrt(n);
	Sieve();
	printf("%d", S(n, 1) + 1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值