Acwing 97. 约数之和
数论,推导,分治
\(O(M*log(x)*L)\)
实数域的分治,多使用折半的方法,这一点在图论上也是一样的
先入暴力,dfs所有约数,发现时间复杂度太高了 \(^_^\)。
其实约数之和不用遍历约数,因为\(A,B\)都是合数,可以得出:
\[A^B = (P_1^{k_1} * p_2^{k_2} * p_3^{k_3} * \ ... \ * p_n^{k_n})^B
	= P_1^{k_1*B} * p_2^{k_2*B} * p_3^{k_3*B} * \ ... \ * p_n^{k_n*B}
\]
那么用乘法分配律,其约数之和可以写成如下形势
\[Sum = (1+P_1^1+P_1^2+P_1^3+...+P_1^{k_1*B})*(1+P_2^1+P_2^2+...+P_2^{k_2*B})*...*
(1+P_n^1+P_n^2+P_n^3+...+P_n^{k_n*B})
\]
取模先不去管他,倘若用快速幂求出每个幂然后相加,如果\(n,B\)取的很大,那么依然会超时。
如果我们能够快速求出每个\(1+P_i^1+P_i^2+P_i^3+...+P_i^{k_i*B}\),那么问题迎刃而解。
那么我们假设:
\[sum(p,c) = 1+p^1+p^2+...+p^c;
\]
当\(c=2*z+1\)时:
\[sum(p,c) = (1+p^1+...+p^{\frac{c-1}{2}})+(p^{\frac{c+1}{2}}+...+p^c)
\]
\[sum(p,c) = (1+p^1+...+p^{\frac{c-1}{2}})+p^{\frac{c+1}{2}}(1+p^1...+p^{\frac{c-1}{2}})
\]
\[sum(p,c) = (1+p^{\frac{c+1}{2}})(1+p^1...+p^{\frac{c-1}{2}})
\]
\[sum(p,c) = (1+p^{\frac{c+1}{2}})*sum(p,\frac{c-1}{2})
\]
同理,当\(c=2*z\)时:
\[sum(p,c) = (1+p^1+...+p^{\frac{c}{2}})+(p^{\frac{c}{2}+1}+...+p^c)
\]
\[sum(p,c) = (1+p^1+...+p^{\frac{c}{2}})+p^{\frac{c}{2}}(p^1+...+p^{\frac{c}{2}})
\]
\[sum(p,c) = (1+p^{\frac{c}{2}})(p^1+...+p^{\frac{c}{2}})+1
\]
\[sum(p,c) = (1+p^{\frac{c}{2}})*(sum(p,{\frac{c}{2}})-1)+1
\]
每次通过分治,将sum分成一半操作,时间复杂度从线性变为\(logx\)级
AC code
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=100010,P=9901;
int n,m;
int qmi(int a,int b,const int p)
{
	int res=1;
	while(b)
	{
		if(b&1) res=(ll)res*a%p;
		a=(ll)a*a%p;
		b>>=1;
	}
	return res%p;
}
int sum(int p,int c)
{
	if(!c) return 1;
	if(c&1) return (ll)(qmi(p,(c>>1)+1,P)+1)*sum(p,c>>1)%P;
	else return (1+(ll)(qmi(p,(c>>1),P)+1)*(sum(p,c>>1)-1))%P;
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	
	cin>>n>>m;
	
	ll res=1;
	for(int i=2;i*i<=n;i++)
    	if(n%i==0)
    	{
    	    int cnt=0;
    		while(n%i==0) cnt++,n/=i;
    		res=(ll)res*sum(i,m*cnt)%P;
    	}
	
	if(n>1) res=res*sum(n,m)%P;
	else if(!n) res=0;
	cout<<res%P;
	
	return 0;
}
走走停停……

                
            
        
浙公网安备 33010602011771号