http://acm.hdu.edu.cn/showproblem.php?pid=1695
[1,b]中选择一个x,[1,d]中选择一个y,使得gcd(x,y) = k问题等价于在[1,b/k],[1,d/k]中选择x,y使得gcd(x,y) = 1
我们保证b小于d,那么就可以遍历[1,d/k]这个区间得到所有满足条件的素数对.
分为两种情况:
1.[1,b]时直接使用欧拉定理计算
2.[b,d]时利用容斥原理来间接计算:b - (有一个共同质因数的总和) + (有两个共同质因数的总和) - (有三个共同质因数的总和)……
那么问题来了:
欧拉函数(积性函数)
对正整数n,欧拉函数是少于或等于n的数中与n互质的数的数目。若n是质数p的k次幂,φ(n)=p^k - p^(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟n互质.
通式:φ(x)=x*(1-1/p1)*(1-1/p2)*(1-1/p3)*(1-1/p4)*…..*(1-1/pn), 其中p1, p2……pn为x的所有质因数,x是不为0的整数.
φ(x)=φ(m)*φ(n)(m*n = x && gcd(m,n) = 1)
代码实现
void init(){
euler[1] = 1;
for(int i=2; i<MAX; i++){
if(!euler[i]){///没有计算过的是素数
for(int j=i; j<MAX; j+=i){
if(!euler[j]) euler[j] = j;
euler[j] -= euler[j]/i;
prime[j][num[j]++] = i;///记录每一个合数分解的素数序列
}
}
///euler[i] += euler[i-1];
}
}
容斥原理
在计数时,必须注意无一重复,无一遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理.
AC代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAX = 100010;
LL euler[MAX];
int prime[MAX][10],num[MAX];
void init(){
euler[1] = 1;
for(int i=2; i<MAX; i++){
if(!euler[i]){
for(int j=i; j<MAX; j+=i){
if(!euler[j]) euler[j] = j;
euler[j] -= euler[j]/i;
prime[j][num[j]++] = i;
}
}
euler[i] += euler[i-1];
}
}
int main(){
init();
int cas,t=0;
scanf("%d",&cas);
while(cas--){
LL a, b, c, d, k, ans;
cin >> a >> b >> c >> d >> k;
if(k == 0) ans = 0;
else{
if(b > d) swap(b, d);
b /= k, d /= k;
ans = euler[b];
for(int i=b+1; i<=d; i++){///容斥原理
int t = num[i], tmp = b;
for(int j=1; j<(1 << t); j++){
int x = 1, y = 1;
for(int k=0; k<t; k++)
if(j & (1 << k))
x *= prime[i][k], y *= -1;
tmp += y*b/x;
}
ans += tmp;
}
}
printf("Case %d: ",++t);
cout << ans << endl;
}
//system("pause");
return 0;
}