前言
失戀了跑過來寫這題,想當年這題咕了好久,現在分分鍾切了。
Solution
通過唯一分解定理:
所以:
通過自己研究研究可以知道的兩個定理:
- 1.約數個數定理:
解釋:從每一個質數里面任選一個次冪(選擇范圍 \([0,\alpha_i]\)),與其他質數相組合就是一個約數,所以約數個數就是上面這個式子。
- 2.約數和定理:
解釋:每個括號里面就是每一個質數的任一個次冪(次冪范圍 \([0,\alpha_i]\))之和,如果把括號拆開,就是每個括號里面選一個數乘起來,再把所有選擇加起來。每個括號選一個數乘起來,不正對應每一個約數的值嗎?所以約數和就是上面這個式子。
上面這個式子有 億點點 難看,我們把它寫簡潔一點就是:
回到題目,在這個題目里的 約數和 就是如下式子:
暴力計算復雜度 \(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路上走下去。
自己選擇的路,跪着也要走完。朋友們,雖然這個世界日益浮躁起來,只要能夠為了當時純粹的夢想和感動堅持努力下去,不管其它人怎么樣,我們也能夠保持自己的本色走下去。 ——陳立傑