Xtreme 10.0 - Inti Sets

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

代码:

/**
 *  质数的范围
 *  由于题目中的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();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值