Xtreme 10.0 - Inti Sets
题目:
https://www.hackerrank.com/contests/ieeextreme-challenges/challenges/inti-sets
题意:
输入三个数A、B、N,其中1 <= A <= B <= N <= 10^12,求出区间[A, B]中与所有N互质的数的总和。
输入:
第一行输入n,代表测试用例的数量。
接下来为每个测试用例的输入,输入N A B,中间以空格分开。
输出:
输出区间 [A, B] 中与所有N互质的数的总和,若该数大于1000000007,输出其与1000000007的模。
测试用例:
输入:
2
12 5 10
5 1 4
输出:
12
10
解释:
对于第一个测试用例,[5, 10] 中与12互质的数只有5和7,因此结果为5 + 7 = 12。
对于第二个测试用例,[1, 4] 中与5互质的数是1, 2,3和4,因此结果为1 + 2 + 3 + 4 = 10。
思路:
方法1:
采用暴力穷举的方法,遍历[A, B],将其中与N互质的数挑选出来进行求和。该方法时间复杂度为O( (B - A) * N )。
方法2:
方法1 中使用的是穷举法,在时间复杂度上不符合要求,因此我们思考下是否有更有效率的解法。
由于题目目的是求和,我们不妨考虑下是否有什么求和公式可以直接用于求[A, B]中与N互质的数的和呢?很遗憾并没有。
接下来再换个思考方向,有没有什么求和公式能够求出求[A, B]中与N不互质的数的和呢?如果有的话,那就能够用[A, B]区间数的总和减去[A, B]中与N不互质的数的和的差来求解了。
我们来看下互质的性质:若两个数互质,则它们的最大公因数为1。相反的,若a与b不互质,其中a < b,则a与b一定有不为1的最大公因数,且该公因数可以由b质因数分解得到。
举个简单的例子,要求
[1, 10]中与12不互质的数的和。对12质因数分解(12 = 2 * 2 * 3),因此12的质因数有2和3。而[1, 10]范围内与不与12互质的数有2, 3, 4, 6, 8, 9, 10,其中2, 4, 6, 8, 10是2的倍数,3, 6, 9是3的倍数。到此,我们可以发现只要求得12的所有质因数2和3,便能够将区间[1, 10]内找到所有的与12不互质的数。
那么问题就转化为2个,一是如何求不与n互质数的和,二是如何分解质因数。
对于第一个求和问题,2, 4, 6, 8, 10和3, 6, 9都是等差数列,他们的和可以直接通过求和公式算出。他们公共的部分的6可以通过容斥定理来排除。在使用容斥定理求和的过程中,注意使用位操作来枚举质因数,奇加偶减来消除重复的部分。
对于第二个分解质数问题,可以先用排除合数的方法,找到范围内的所有质数。由于N的范围限定在10^12内,因此只需要求出10^6内的所有质数。而在使用限定范围内的质数分解N时,要注意分解出来的最后一位质因数可能大于10^6,小于10^12次方,要将该数加进结果中。
方法2具体步骤:
1、对N进行质因数分解。
2、使用分解出来的质因数通过求倍数的方式筛选出区间 [1, B] 与N不互质的数,用等差数列求和公式算出他们的和,并用容斥定理排除重复计算的部分。最终得出求区间 [1, B] 与N不互质的数的和。
3、
区间 [1, B] 中所有数的总和 -
区间 [1, B] 中与N不互质的数的总和 =
区间 [1, B] 中与所有N互质的数的总和。
4、用同样的方法求出 区间 [1, A - 1] 中与所有N互质的数的总和。
5、区间 [A, B] 中与所有N互质的数的总和 = 区间 [1, B] 中与所有N互质的数的总和 - 区间 [1, A - 1] 中与所有N互质的数的总和。
最后要注意的是,为了避免溢出,每次加、减、乘法运算时都要注意取模。取模运算的规则有:
(1) (a + b) % c = (a % c + b % c) % c
(2) (a b) % c = (a % c) (b % c) %c
(2) (a b) % c = (a % c) (b % c) %c
代码:
/**
* 质数的范围
* 由于题目中的n为待因素分解的数,且n < 1e12,
* 所以只需要用sqrt(1e12) = 1e6范围内的所有质数去分解n就好了
*/
const long long maxPriNum = (long long)1e6;
/**求模的数*/
const long long modNum = (long long)1000000007;
/**1-maxPriNum范围内的所有质数*/
vector<long long> g_prineNumbers;
/**0代表合数,1代表质数*/
bitset<maxPriNum> g_primeNumMapSet;
/**
* 求指定范围内的所有质数
*/
void getAllPrimes() {
g_prineNumbers.clear();
g_primeNumMapSet.set();
for (long long i = 2; i < maxPriNum; i++) {
//0代表合数
if (g_primeNumMapSet[i] == 0) continue;
//1代表质数
for (long long j = i * i; j < maxPriNum; j += i) {
g_primeNumMapSet[j] = 0;
}
g_prineNumbers.push_back(i);
}
}
/**
* 输出所有质数
*/
void printAllPrimes() {
for (vector<long long>::iterator it = g_prineNumbers.begin();
it != g_prineNumbers.end(); it++) {
cout << *it << endl;
}
cout << endl;
}
/**
* 质因数分解(无重复因数)
*/
vector<long long> primeFactorizationUnique(long long num) {
vector<long long> res;
if (num == 1) {
res.push_back(1);
return res;
}
long long n = num;
for (vector<long long>::iterator it = g_prineNumbers.begin();
it != g_prineNumbers.end(); it++) {
if (n == 1) break;
bool isDivided = false;
while (n % (*it) == 0) {
isDivided = true;
n /= *it;
}
if (isDivided) res.push_back(*it);
}
//注意:由于是使用[1, 10^6]的数去分解[1, 10^12]的大数,因此还可能剩下一个[10^6-10^12]的因子
//所以要把最后这个因子给加进去
if (n > 1) res.push_back(n);
return res;
}
/**
* 质因数分解(有重复因数)
*/
vector<long long> primeFactorization(long long num) {
vector<long long> res;
if (num == 1) {
res.push_back(1);
return res;
}
long long n = num;
for (vector<long long>::iterator it = g_prineNumbers.begin();
it != g_prineNumbers.end(); it++) {
if (n == 1) break;
while (n % (*it) == 0) {
res.push_back(*it);
n /= *it;
}
}
//注意:由于是使用[1, 10^6]的数去分解[1, 10^12]的大数,因此还可能剩下一个[10^6-10^12]的因子
//所以要把最后这个因子给加进去
if (n > 1) res.push_back(n);
return res;
}
/**
* 求区间[1, x]所有数的和
*/
long long sumOfRange(long long x) {
//注意:为了避免数字溢出,相乘前先取模
x %= modNum;
return (x * (x + 1) >> 1) % modNum;
}
/**
* 求区间[1, n]内,x以及其倍数的和
*/
long long sumOfMulti(long long x, long long n) {
long long divN = n / x;
//注意:为了避免数字溢出,相乘前先取模
divN %= modNum;
/**1-divN的和求模后的结果*/
long long tmp = ((((divN + 1) % modNum) * divN) >> 1) % modNum;
//等差数列公式
return ((((divN + 1) * divN) >> 1) % modNum * x) % modNum;
}
/**
* 求区间[1, x]内所有与n不互质的数的和
* 分析:a是区间[1, x]内的一个数
* 若a和n不互质,则a一定是n分解出来的其中一个质因数(或其倍数)
* 所以 区间[1, x]内所有与n不互质的数的和就等于n分解出来所有质因数及其倍数的和
*/
long long sumOfNotRelativelyPrime(long long x, long long n) {
vector<long long> factors = primeFactorizationUnique(n);
vector<long long>::size_type factorSize = factors.size();
long long sum = 0;
for (int i = 1; i < (1 << factorSize); i++) {
//位运算选取因子的组合
long long factorEnumNum = 1;
long long count = 0;
for (int j = 0; j < factorSize; j++) {
if ((i >> j) & 1) {
count++;
factorEnumNum *= factors[j];
}
}
if (count & 1) {
//奇数加
sum += sumOfMulti(factorEnumNum, x);
sum %= modNum;
}
else {
//偶数减
sum -= sumOfMulti(factorEnumNum, x);
sum += modNum;
sum %= modNum;
}
}
sum %= modNum;
return sum;
}
/**
* 求区间[1, right]内所有与n互质的数的和
*/
long long sumOfRelativelyPrime(long long right, long long n) {
return (sumOfRange(right) - sumOfNotRelativelyPrime(right, n) + modNum) % modNum;
}
/**
* 求区间[left, right]内所有与n互质的数的和
*/
long long sumOfRelativelyPrime(long long left, long long right, long long n) {
if (left == 1) return sumOfRelativelyPrime(right, n);
return (sumOfRelativelyPrime(right, n) - sumOfRelativelyPrime(left - 1, n) + modNum) % modNum;
}
void intiSetsOneCase() {
long long a = 0, b = 0, n = 0;
cin >> n >> a >> b;
long long res = sumOfRelativelyPrime(a, b, n);
cout << res << endl;
}
void IntiSets() {
getAllPrimes();
int caseCount = 0;
cin >> caseCount;
while (caseCount) {
intiSetsOneCase();
caseCount--;
}
return;
}
int main() {
IntiSets();
}