题目
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4279
题目来源:2012天津网络赛,其实不简单,但是由于是B华丽丽地成了前三题。
简要题意: f(n)=|{x∣gcd(x,n)≠1∧x∤n∧x∈[1,n]}|
即 n 内与n 不互质且不能整除 n 的数的个数。
求 [x,y] 内 f(n) 为奇数的数的个数。 数据范围: T⩽2000;0<x,y<263
题解
在我看来,这是一道很好的数论题目,运用到了不少的知识,但是我第一次看到的时候没能写出来。
定义 τ(x) 为 x 的正除数个数,
φ(x) 为欧拉函数,即小于等于 x 与x 互质的数的个数。则易知 f(n)=n−φ(n)−τ(n)+1 , 1 是约数也和
n 互质,要再加上。首先需要用到的结论是 2∣φ(x)(x>2) ,即大于2的数的欧拉函数值为偶数。
证明: 2∣φ(x)(x>2)
x=pa11pa22⋯pakk;φ(x)=∏i=1k(pi−1)pa1−1i若 k=1,p1=2 则 a1>1,2∣pa1−11,2∣φ(x)因为 2 以外正质数都是奇数,所以除此之外的情况必有
2∤pi ,则 2∣(pi−1),2∣φ(x)因而 2∣φ(x)(x>2)
f(n)(n>2) 为奇数的充要条件变为 n+1−τ(n) 为奇数, f(1),f(2) 易知是不满足条件的。
对于奇数 n 我们需要
τ(n) 为偶数。
对于偶数 n 我们需要τ(n) 为奇数。沿用之前 x,pi,ai 我们可以知道有以下公式:
τ(x)=∏i=1k(ai+1)τ(x) 为奇数当且仅当任意 ai 为偶数,显然此时的 x 一定是个完全平方数。对于奇数
n 我们需要 n 为完全平方数。
对于偶数n 我们需要 n 不为完全平方数。考虑数的平方和自己奇偶性一致,只知道
n−−√ 内奇数偶数的个数就能知道答案。结果就是 (n 内偶数的个数 −1)−( n−−√ 内偶数的个数 )+(n−−√ 内奇数的个数 −1) 需要特判 1,2 。
让结果为 Ans ,可得最终公式为:
Ans={0 n∈{1,2}⌊n/2⌋−⌊n−−√/2⌋+⌊(n−−√−1)/2⌋ else
实现
结论出来就很好搞了,但是取根号这个地方有一些精度问题,可以用
long double
或者是用二分,训练的时候就被精度卡了。复杂度 Θ(logn) 就是开方的复杂度。
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {LL res=1;a%=MOD;for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
// head
LL query(LL x) {
LL sq = sqrt((long double)x);
LL ans = x > 2 ? x/2-1 : 0;
ans -= sq/2;
ans += (sq-1)/2;
return ans;
}
int main()
{
int t;
LL l, r;
scanf("%d", &t);
while (t--) {
scanf("%I64d%I64d", &l, &r);
printf("%I64d\n", query(r)-query(l-1));
}
return 0;
}