POJ 1845 Sumdiv 题解

文章讲述了如何利用质因数分解和分治法,结合快速幂算法求解A^B对9901取模的问题,强调了时间复杂度的控制和编程技巧,如POJ限制和避免使用自动函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目传送门

题目大意

A B  mod  9901 A^B~\text{mod }9901 AB mod 9901的值。

题目解答

先对 A A A 进行质因数分解,可以得到 A = p 1 c 1 p 2 c 2 . . . p n c n A=p_1^{c_1}p_2^{c_2}...p_n^{c_n} A=p1c1p2c2...pncn
那么对于 A B A^B AB 而言它的质因数分解应该是 A = p 1 c 1 B p 2 c 2 B . . . p n c n B A=p_1^{c_1B}p_2^{c_2B}...p_n^{c_nB} A=p1c1Bp2c2B...pncnB
由乘法分配律可知 A B A^B AB 全部的因数之和应该是
∏ i = 1 n ∑ j = 0 c i B p i \prod_{i=1}^{n}\sum_{j=0}^{c_iB}p_i i=1nj=0ciBpi

注意到每一个 Σ \Sigma Σ 都是一个等比数列,但是 9900 9900 9900 不是质数,所以不能求逆元。

这时候我们就可以引入分治法来解决问题。

假设 c i B = k c_iB = k ciB=k Σ \Sigma Σ 部分我们用 s u m ( p , k ) sum(p,k) sum(p,k) 代替。

k k k 为奇数时,展开 s u m ( p , k ) sum(p,k) sum(p,k) ( 1 + p + p 2 + p 3 + . . . + p k ) (1+p+p^2+p^3+...+p^k) (1+p+p2+p3+...+pk)

提出一个 p k + 1 2 p^{\frac{k+1}{2}} p2k+1 得原式 = ( 1 + p + p 2 + . . . + p k − 1 2 ) × ( 1 + p k + 1 2 ) = (1+p+p^2+...+p^{\frac{k-1}{2}})\times{(1+p^{\frac{k+1}{2}})} =(1+p+p2+...+p2k1)×(1+p2k+1)

= s u m ( p , ( k − 1 ) / 2 ) × ( 1 + p k + 1 2 ) =sum(p,(k-1)/2)\times{(1+p^{\frac{k+1}{2}})} =sum(p,(k1)/2)×(1+p2k+1)

类似的,当 k k k 为偶数时,原式 = s u m ( p , k / 2 − 1 ) × ( 1 + p k 2 ) + p k =sum(p,k/2-1)\times(1+p^{\frac{k}{2}})+p^k =sum(p,k/21)×(1+p2k)+pk

其中幂运算可以用快速幂来解决。时间复杂度应为 O ( A log ⁡ 2 B ) O(A\log^2B) O(Alog2B)

技术细节

1.POJ 不支持万能头和 auto。

2.在分解质因数时只能用 O ( n ) O(\sqrt{n}) O(n ) 的复杂度,不可偷懒。

3.建议配合 map 食用质因数分解。

代码实现

//1.NO BITS/STDC++.H
//2.USE THE BEST ALGORITHM YOU KNOW 
//3.NO AUTO
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
typedef long long ll;
#define P 9901
ll a,b;
map<ll,ll> pri;
ll qpow(ll x,ll y)
{
	ll r=1;
//	x%=P;
	while(y)
	{
		if(y&1) r=r*x%P;
		x=x*x%P,y>>=1;
	}
	return r;
}
ll sigma(ll x,ll y)
{
	if(y==0) return 1;
	if(y&1)
	{
		return /*r=*/((qpow(x,(y+1)>>1)+1)%P*sigma(x,(y-1)>>1)%P);
	}
	else
	{
		return /*r=*/((qpow(x,y>>1)+1)%P*sigma(x,(y>>1)-1)%P+qpow(x,y))%P;
	}
//	cout<<x<<' '<<y<<' '<<r<<endl;
}
int main()
{
	scanf("%lld %lld",&a,&b);
	if(a==0)
	{
		printf("0\n");
		return 0;
	}
	ll sum=1;
	for(ll i=2;i<=a/i;i++)
	{
		while(a%i==0)
			a/=i,++pri[i];
	}
	if(a!=1)
		pri[a]++; 
	for(map<ll,ll>::iterator i=pri.begin();i!=pri.end();i++)
	{
		sum*=sigma(i->first,i->second*b)%P;
		sum%=P;
	}
	printf("%lld\n",sum);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值