Luogu P3601 签到题

( ext{Problem})

签到题

( ext{Analysis})

区间筛积性函数,如 (varphi)
因为 (r-l le 10^6),所以这个问题就是非常经典的了
比较常见的技巧
(varphi(n)=nprod(1-frac 1 p))
([l,r]) 以内的数若不为素数分解质因数后最小的必然小于 (sqrt r)
(sqrt r) 以内的素数算贡献即可
顺便除掉素数,就可以找到 (sqrt r) 以外的素数

( ext{Code})

#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long LL;

const int N = 1e6 + 5;
const LL P = 666623333;
int tot;
LL l, r, prime[N], num[N], vis[N];

void getprime(int Mx)
{
	vis[1] = 1;
	for(int i = 2; i <= Mx; i++)
	{
		if (!vis[i]) prime[++tot] = i;
		for(int j = 1; j <= tot && prime[j] * i <= Mx; j++)
		{
			vis[prime[j] * i] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

LL solve()
{
	for(int i = 1; i <= r - l + 1; i++) vis[i] = num[i] = l + i - 1;
	for(int i = 1; i <= tot && prime[i] <= r; i++)
	{
		for(LL j = max(1LL, l / prime[i]); j * prime[i] <= r; j++)
		if (j * prime[i] >= l) 
		{
			vis[j * prime[i] - l + 1] = vis[j * prime[i] - l + 1] - vis[j * prime[i] - l + 1] / prime[i];
			while (num[j * prime[i] - l + 1] % prime[i] == 0)
				num[j * prime[i] - l + 1] /= prime[i];
		}
	}
	for(int i = 1; i <= r - l + 1; i++)
	if (num[i] > 1) vis[i] = vis[i] - vis[i] / num[i];
	LL ans = 0;
	for(int i = 1; i <= r - l + 1; i++) ans = (ans + (l + i - 1 - vis[i])) % P;
	return ans;
}

int main()
{
	scanf("%lld%lld", &l, &r);
	getprime(sqrt(r) + 5);
	printf("%lld
", solve());
}

( ext{Addition})

区间筛 (mu)
同样的有 (r-l le 10^6)
同理,利用积性函数的性质即可

inline void solve_u(ll l, ll r)
{
	for(int i = 1; i <= r - l + 1; i++) mu[i] = 1, num[i] = i + l - 1;
	for(int i = 1; i <= totp && prime[i] <= r; i++)
	{
		for(ll j = max(l / prime[i], 1ll); j * prime[i] <= r; j++)
		if (j * prime[i] >= l)
		{
			if (j % prime[i] == 0) mu[j * prime[i] - l + 1] = 0;
			else mu[j * prime[i] - l + 1] *= -1, num[j * prime[i] - l + 1] /= prime[i];
		}
	}
	for(int j = 1; j <= r - l + 1; j++)
	if (mu[j] != 0 && num[j] != 1) mu[j] *= -1;
}
原文地址:https://www.cnblogs.com/leiyuanze/p/14977494.html