原题链接:https://www.luogu.com.cn/problem/P3601
题意解读:求l~r范围内所有qiandao(x) 之和,qiandao(x)为小于等于x的数中,与x不互质的数的个数。注意取模。
解题思路:
欧拉函数定义:phi(x) = x * (1-1/p1) * (1-1/p2) *...* (1-1/pn),p1,p2...pn为x的所有质因子
其中:phi(x)表示1~x中所有与x互质的数的个数
因此:qiandao(x) = x - phi(x)
对于l,r≤107的情况,可以用线性筛的方式,边筛素数,边通过递推计算phi,能得到60分。
对于l≤r≤1012的情况,
1、先把sqrt(r)范围内所有素数筛出来(因为r范围内所有的素因子只可能有一个大于sqrt(r)或者没有,其余都小于sqrt(r),筛出小的素因子,单独处理大的素因子)
2、在对每一个素数p,枚举l ~ r内是p的倍数的数j
3、phi[j]乘上素数p对phi[j]的贡献,即(1-1/p) = (p-1)/p
4、用一个数组pb[]存储所有l~r之间的数x可能超过sqrt(x)的素因子
5、在计算p对phi[j]的贡献的同时,将pb[j]的所有p因子除尽,剩下的要么是1,要么就是大于sqrt(j)的素因子
6、最后处理pb中素因子对phi的影响
7、计算答案
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
const LL MOD = 666623333;
LL l, r;
LL primes[N], cnt, flag[N];
LL phi[N], pb[N]; //phi[x]表示φ(x)欧拉函数的值,pb[i]表示i的大于sqrt(i)的那一个质因数
LL ans;
//埃氏筛,筛1~10^6范围内所有素数
void getprimes()
{
for(LL i = 2; i <= 1000000; i++)
{
if(!flag[i])
{
primes[++cnt] = i;
for(LL j = i + i; j <= 1000000; j += i)
{
flag[j] = true;
}
}
}
}
int main()
{
cin >> l >> r;
//筛1~sqrt(r)范围内所有素数
getprimes();
//初始化phi,pb
for(LL i = l; i <= r; i++)
{
phi[i - l] = i;
pb[i - l] = i;
}
//计算phi,pb
for(LL i = 1; i <= cnt && primes[i] * primes[i] <= r; i++)
{
LL p = primes[i];
LL start = l / p;
if(l % p != 0) start++;
for(LL j = start * p; j <= r; j += p) // j是l~r范围内p的倍数
{
phi[j - l] = phi[j - l] / p * (p - 1); //计算素数p对phi[j]的贡献
while(pb[j - l] % p == 0) pb[j - l] /= p; //将pb[j]去掉所有p素因子
}
}
for(LL i = l; i <= r; i++)
{
if(pb[i - l] != 1)
{
phi[i - l] = phi[i - l] / pb[i - l] * (pb[i - l] - 1);
}
ans = (ans + (i - phi[i - l])) % MOD;
}
cout << ans;
return 0;
}
标签:phi,签到,洛谷题,LL,素数,sqrt,pb,因子,P3601 From: https://www.cnblogs.com/jcwy/p/18138388