AcWing 97. 约数之和(分治法进行等比数列求和)

题目链接:点击这里
AcWing 97. 约数之和(分治法进行等比数列求和)_第1张图片

约数的基础知识:点击这里

A A A 分解质因数,表示为 p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p n c n p_1^{c_1}*p_2^{c_2}*...*p_n^{c_n} p1c1p2c2...pncn

那么 A B A^B AB 可表示为 p 1 B ∗ c 1 ∗ p 2 B ∗ c 2 ∗ . . . ∗ p n B ∗ c n p_1^{B*c_1}*p_2^{B*c_2}*...*p_n^{B*c_n} p1Bc1p2Bc2...pnBcn

A B A^B AB 的所有约数可表示为集合 { p 1 k 1 ∗ p 2 k 2 ∗ . . . ∗ p n k n } \left\{p_1^{k_1}*p_2^{k_2}*...*p_n^{k_n}\right\} {p1k1p2k2...pnkn},其中 0 ≤ k i ≤ B ∗ c i   ( 1 ≤ i ≤ n ) 0≤k_i≤B*c_i\ (1≤i≤n) 0kiBci (1in)

根据乘法分配律, A B A^B AB 的所有约数之和就是:

( 1 + p 1 + p 1 2 + . . . + p 1 B ∗ c 1 ) ∗ ( 1 + p 2 + p 2 2 + . . . + p 2 B ∗ c 2 ) ∗ . . . ∗ ( 1 + p n + p n 2 + . . . + p n B ∗ c n ) (1+p_1 +p_1^2+...+p_1^{B*c_1})*(1 +p_2+p_2^2+...+p_2^{B*c_2})*...*(1+p_n+p_n^2+...+p_n^{B*c_n}) (1+p1+p12+...+p1Bc1)(1+p2+p22+...+p2Bc2)...(1+pn+pn2+...+pnBcn)

上式中的每个括号内都是等比数列,如果使用等比数列求和公式,需要做除法。而答案还需要对 9901 9901 9901 取模,mod 运算只对加、减、乘有分配率,不能直接对分子分母分别取模后再做除法。

我们可以换一种思路,使用分治法进行等比数列求和。所谓分治法,就是把一个问题划分为若干个规模更小的同类子问题,对这些子问题递归求解,然后在回溯时通过它们推导出原问题的解。

问题:使用分治法求 s u m ( p , c ) = 1 + p + p 2 + . . . + p c = ? sum(p,c)=1+p+p^2 +...+p^c= ? sum(p,c)=1+p+p2+...+pc=?

c c c 为奇数:

  • s u m ( p , c ) = ( 1 + p + . . . + p c − 1 2 ) + ( p c + 1 2 + . . . + p c ) sum(p,c)=(1+p+...+p^{\frac{c-1}{2}})+(p^{\frac{c+1}{2}}+...+p^c) sum(p,c)=(1+p+...+p2c1)+(p2c+1+...+pc)$
                       = ( 1 + p + . . . + p c − 1 2 ) + p c + 1 2 ∗ ( 1 + p + . . . + p c − 1 2 ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(1+p+...+p^{\frac{c-1}{2}})+p^{\frac{c+1}{2}}*(1+p+...+p^{\frac{c-1}{2}})                   =(1+p+...+p2c1)+p2c+1(1+p+...+p2c1)
                       = ( 1 + p c + 1 2 ) ∗ s u m ( p , c − 1 2 ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =(1+p^{\frac{c+1}{2}})*sum(p,\frac{c-1}{2})                   =(1+p2c+1)sum(p,2c1)

c c c 为偶数,类似地:

  • s u m ( p , c ) = ( 1 + p c 2 ) ∗ s u m ( p , c 2 − 1 ) + p c sum(p,c)=(1 +p^{\frac{c}{2}}) * sum(p,\frac{c}{2}-1) + p^c sum(p,c)=(1+p2c)sum(p,2c1)+pc

每次分治(递归之后),问题规模均会缩少一半,配合快速幂即可在 O ( l o g c ) O(logc) O(logc) 的时间内求出等比数列的和。

#include
#include
#include
#include
#include

using namespace std;

const int mod = 9901;

int Pow(int a, int b)
{
	a %= mod;
	int res = 1;
	while(b)
	{
		if(b&1)
			res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

int sum(int p, int c)
{
	if(c==0)
		return 1;
	if(c&1)
		return (1 + Pow(p, (c+1)/2)) * sum(p, (c-1)/2) % mod;
	else
		return (Pow(p, c) + (1 + Pow(p, c/2)) * sum(p, c/2-1)) % mod;
}

int main()
{
	int A, B;
	scanf("%d%d", &A, &B);
	
	int ans = 1;	
	for(int i = 2; i <= A; ++i)
	{
		int cnt = 0;		//A里面有cnt个因子i 
		while(A % i == 0)
		{
			cnt++;
			A /= i;
		}
		if(cnt)
			ans = ans * sum(i, cnt * B) % mod;
	}
	
	if(!A)	ans = 0;
	printf("%d\n", ans);
	
	return 0;
}

你可能感兴趣的:(数论)