洛谷P3601 签到题
题目描述
我们定义一个函数:qiandao(x)为小于等于x的数中与x不互质的数的个数。
这题作为签到题,给出l和r,要求求 ∑ i = l r q i a n d a o ( i ) m o d 666623333 \sum_{i=l}^r qiandao(i) mod 666623333 ∑i=lrqiandao(i)mod666623333.
输入格式
一行两个整数,l、r。
输出格式
一行一个整数表示答案。
输入输出样例
输入 #1
233 2333
输出 #1
1056499
说明/提示
对于100%的数据, 1 ≤ l ≤ r ≤ 1 0 12 , r − l ≤ 1 0 6 1\leq l\leq r\leq 10^{12},r-l\leq 10^6 1≤l≤r≤1012,r−l≤106.
这题一看就是要求l到r范围内所有x,与x不互质且小于等于x的数个数。一看数据范围,l和r是
1
0
12
10^{12}
1012范围,暴力不太行,但r-l的范围却是可观的。考虑用数组phi[i]存储与l+i互质且小于等于i的元素个数。最关键的是欧拉函数的公式别忘记了:
φ
(
x
)
=
x
∏
i
=
1
n
(
1
−
1
p
i
)
\varphi(x)=x\prod_{i=1}^n(1-\frac{1}{p_i})
φ(x)=xi=1∏n(1−pi1)
其中
p
1
,
p
2
,
⋯
,
p
n
p_1,p_2,\cdots,p_n
p1,p2,⋯,pn为x所有质因数。那么接下来就需要求l到r范围内数的所有质因数,一个个遍历不太好,比较好的办法是先预处理出来
1
0
6
10^6
106范围内的素数(基础的线性筛),然后遍历这些素数,将i的phi初始化为i,如果i含有这个素因子就减小。代码如下:
- now标记素数 数组primes当前的位置,isprime用于预处理素数。
- phi[i]表示i+l的欧拉函数,remain[i]表示i+l除掉之前的素因子之后还剩下什么。由于处理 1 0 6 10^6 106以上的素因子开销太大,而这么大的素因子的个数至多一个,所以如果最后remain[i]>1,就说明这是一个大于 1 0 6 10^6 106的素因子。
#include<bits/stdc++.h>
using namespace std;
long l,r,now=1,res=0,mod=666623333;
long isprime[1000010],primes[1000010];
long phi[1000010],remain[1000010];
void build(){
isprime[1]=0;
for(int i=2;i<=1e6;i++){
if(isprime[i]){
primes[now++]=i;
}
for(int j=1;j<now&&primes[j]*i<=1e6;j++){
isprime[primes[j]*i]=0;
if(i%primes[j]==0) break;
}
}
}
int main(){
cin>>l>>r;
memset(isprime,1,sizeof(isprime));
build();
for(long i=l;i<=r;i++){
phi[i-l]=i;
remain[i-l]=i;
}
for(long i=1;i<now&&primes[i]*primes[i]<=r;i++){
long p=primes[i];
for(long j=((l-1)/p+1)*p;j<=r;j+=p){
phi[j-l]=phi[j-l]/p*(p-1);
while(remain[j-l]%p==0) remain[j-l]/=p;
}
}
for(long i=l;i<=r;i++){
if(remain[i-l]>1){
phi[i-l]=phi[i-l]/remain[i-l]*(remain[i-l]-1);
}
res=(res+i-phi[i-l])%mod;
}
cout<<res;
return 0;
}