luogu3601 签到题

题目大意

\[\sum_{i=l}^{r}(i-\varphi(i)) \]

\(l,r\leq 10^{12}, r-l<10^6\)

思路

看到这个数据范围,不能直接用线性筛。我们要想求\(\varphi\),需要用时间复杂度为根号的算法(往往是反演)。要想反演,我们要由最朴素的方法中找到思路。
以下是求一个\(\varphi(x)\)值的普速算法的伪代码。

for the x:
    foreach prime p (in [1, sqrt(x)] && p|x)
        deal(&x, p)
    if(x>1)
        deal_as_big_prime(x)

我们发现枚举的\(p\)都在\(\sqrt{x}\)的范围内。因此我们可以利用每一个\([1,\sqrt{r}]\)内的质数求出\([l,r]\)之间的所有\(\varphi\)值。伪代码:

foreach prime p (in [1, sqrt(r)])
    foreach x[i] (i in [l, r] && p|x[i])//i是x[i]的初始值,x[i]以后会改变
        deal(&x[i], p)
foreach x[i] (x[i]>1)
    deal_as_big_prime(x[i])

完整代码:

#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

#define ll long long
#define Phi(i) Phis[i - l]
#define X(i) Xs[i - l]

const int MAX_N = 1000010, P = 666623333;
ll Phis[MAX_N], Xs[MAX_N];
int Primes[MAX_N];

int GetPrime(int *prime, int n)
{
	static bool NotPrime[MAX_N];
	int primeCnt = 0;
	for (int i = 2; i <= n; i++)
	{
		if (!NotPrime[i])
			prime[primeCnt++] = i;
		for (int j = 0; j < primeCnt; j++)
		{
			if (i*prime[j] > n)
				break;
			NotPrime[i*prime[j]] = true;
			if (i%prime[j] == 0)
				break;
		}
	}
	return primeCnt;
}

void GetPhi(ll l, ll r, int primeCnt)
{
	for (ll i = l; i <= r; i++)
		Phi(i) = X(i) = i;
	for (int i = 0; i < primeCnt; i++)
	{
		ll lb = Primes[i] * (l / Primes[i]), rb = Primes[i] * (r / Primes[i]);
		if (lb < l)
			lb += Primes[i];
		for (ll j = lb; j <= rb; j+=Primes[i])
		{
			Phi(j) = Phi(j) / Primes[i] * (Primes[i] - 1);
			while (X(j) % Primes[i] == 0)
				X(j) /= Primes[i];
		}
	}
	for (ll j = l; j <= r; j++)
		if (X(j) > 1)
			Phi(j) = Phi(j) / X(j) * (X(j) - 1);
}

ll StatPhi(ll l, ll r)
{
	ll ans = 0;
	for (ll i = l; i <= r; i++)
		ans = (ans + i - Phi(i)) % P;
	return ans;
}

int main()
{
	ll l, r;
	scanf("%lld%lld", &l, &r);
	int primeCnt = GetPrime(Primes, sqrt(r));
	GetPhi(l, r, primeCnt);
	printf("%lld\n", StatPhi(l, r));
	return 0;
}
posted @ 2018-05-07 22:46  headboy2002  阅读(115)  评论(0编辑  收藏  举报