約數之和 (數論)


前言

失戀了跑過來寫這題,想當年這題咕了好久,現在分分鍾切了。

ACwing題目地址

Solution

通過唯一分解定理:

\[A=p^{\alpha_1}_1*p^{\alpha_2}_2*...*p^{\alpha_n}_n \]

所以:

\[A^B=p^{\alpha_1*B}_1*p^{\alpha_2*B}_2*...*p^{\alpha_n*B}_n \]

通過自己研究研究可以知道的兩個定理

  • 1.約數個數定理:

\[Num(A)=(1+\alpha_1)*(1+\alpha_2)*...*(1+\alpha_n) \]

解釋:從每一個質數里面任選一個次冪(選擇范圍 \([0,\alpha_i]\)),與其他質數相組合就是一個約數,所以約數個數就是上面這個式子。

  • 2.約數和定理:

\[Sum(A)=(1+p_1^1+p_1^2+...+p_1^{\alpha_1})*(1+p_2^1+p_2^2+...+p_2^{\alpha_2})*...*(1+p_n^1+p_n^2+...+p_n^{\alpha_n}) \]

解釋:每個括號里面就是每一個質數的任一個次冪(次冪范圍 \([0,\alpha_i]\))之和,如果把括號拆開,就是每個括號里面選一個數乘起來,再把所有選擇加起來。每個括號選一個數乘起來,不正對應每一個約數的值嗎?所以約數和就是上面這個式子。

上面這個式子有 億點點 難看,我們把它寫簡潔一點就是:

\[Sum(A)=\prod_{i=1}^n (\sum_{j=0}^{\alpha_i}p_i^j) \]

回到題目,在這個題目里的 約數和 就是如下式子:

\[Ans=\prod_{i=1}^n (\sum_{j=0}^{\alpha_i*B}p_i^j) \]

暴力計算復雜度 \(O(n^2)\),還要帶上個質因數分解的復雜度。本題數據 \(n<=5*10^7\),因此要想辦法優化。

發現 \(\sum_{j=0}^{\alpha_i*B}p_i^j\) 不就是一個 等比數列求和 嗎? 等比數列求和公式

注意在 模意義 下,除一個數要用乘它的逆元代替,模數 \(9901\) 是一個素數,可以用 費馬小定理 直接求得逆元。 (這題目要用的東西還挺多的

所以最后分別求出 \(n\) 個等比數列求和,全部乘起來就是答案。每個等比數列 \(a_0=1,q=p_i\)\(q\) 是公比)。這樣我們就可以求出整個題目的解了。

求一次乘法逆元的復雜度是 \(O(\log MOD)\),因為 \(MOD\) 較小,可以忽略記為常數;求一次快速冪的復雜度是 \(O(\log N)\) 這個應該小於 \(32\)。所以總復雜度應該是 \(O(9 * \log n)\) (大霧 (有一個著名的定理 \(n<=10^9\) 分解出來的質數不超過9個)

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define MOD 9901
#define int long long
using namespace std;
inline int read() {
	int x=0,f=1; char ch=getchar();
	while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
	return x * f;
}
int A,B;
vector<pair<int,int> > p;
int Pow(int x,int y) {	//9901是質數,直接上費馬小 
	int res = 1, base = x;
	while(y) {
		if(y&1) res = (res*base)%MOD; base = (base*base)%MOD; y >>= 1;
	}
	return res;
}
signed main()
{
	A = read(), B = read();
	if(A == 0) {
		puts("0"); return 0;
	}
	while(A > 1) {
		for(int i=2;i<=A;++i) {
			if(A%i == 0) {
				int num = 0;
				while(A%i == 0) A /= i, ++num;
				p.push_back(make_pair(i,num));
				break;
			}
		}
	}
	int ans = 1, inv, S;
	for(int i=0;i<p.size();++i) {
		inv = Pow(p[i].first-1+MOD%MOD,MOD-2);
		S = (Pow(p[i].first, p[i].second*B+1)-1+MOD)%MOD;
		S = (S*inv)%MOD;
		ans = (ans*S)%MOD;
	}
	printf("%lld\n",ans);
	return 0;
}

Summary

水題一道,只是做來安慰安慰自己的心情,使得自己能堅定地在OI路上走下去。

自己選擇的路,跪着也要走完。朋友們,雖然這個世界日益浮躁起來,只要能夠為了當時純粹的夢想和感動堅持努力下去,不管其它人怎么樣,我們也能夠保持自己的本色走下去。 ——陳立傑


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM