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;
}
posted @ 2023-01-15 12:59  Sankano  阅读(58)  评论(0)    收藏  举报