分治法
在做这题之前,先补充几个背景的题目
有了约数和的知识背景基础,下面就来介绍分治法求$p^0 + p^1 + \ldots + p^{k-1}$
思路
这里实现一个sum函数,sum(p, k)表示$p^0 + p^1 + \ldots + p^{k-1}$
思路,当k为偶数时,sum(p, k)可以拆解成
$$p^0 + p^1 + \ldots + p^{k/2-1} + p^{k/2} +p^{k/2 + 1} + \ldots + p^{k-1}$$
即
$$p^0 + p^1 + \ldots + p^{k/2-1} + p^{k/2} \* (p^0 + p^1 + \ldots + p^{k/2-1})$$
也就是
$$sum(p, k / 2) + p^{k/2} \* sum(p, k / 2)$$
进一步化简
$$(p^{k/2} + 1) \* sum(p, k / 2)$$
当k为奇数时,为了更方便调用我们写的偶数项情况,可以单独拿出最后一项,把剩下的项转化为求偶数项的情况来考虑,再加上最后一项,就是奇数项的情况了。也即$sum(p, k - 1)+p^{k-1}$
这样这个代码就写完了
//p0 + .. + pk-1
int sum(int p, int k) {
if(k == 1) return 1; //边界
if(k % 2 == 0) {
return (LL)(qmid(p, k / 2) + 1) * sum(p, k / 2) % mod;
}
return (qmid(p, k - 1) + sum(p, k - 1)) % mod;
}
完整代码
#include<iostream>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int mod = 9901;
int A, B;
//保存质因子以及出现的次数
unordered_map<int, int> primes;
//试除法质因子分解
void divide(int n) {
for(int i = 2; i <= n / i; i++) {
if(n % i == 0) {
while(n % i == 0) {
primes[i]++;
n /= i;
}
}
}
if(n > 1) {
primes[n]++;
}
}
//快速幂
int qmid(int a, int b) {
int res = 1;
while(b) {
if(b & 1) res = (LL)res * a % mod;
a = (LL)a * a % mod;
b >>= 1;
}
return res;
}
//p0 + .. + pk-1
int sum(int p, int k) {
if(k == 1) return 1; //边界
if(k % 2 == 0) {
return (LL)(qmid(p, k / 2) + 1) * sum(p, k / 2) % mod;
}
return (qmid(p, k - 1) + sum(p, k - 1)) % mod;
}
int main(){
cin >> A >> B;
//对A分解质因子
divide(A);
int res = 1;
for(auto it : primes) {
//p是质因子,k是质因子的次数
int p = it.first, k = it.second * B;
// res要乘上每一项, 注意这里是k + 1
res = (LL)res * sum(p, k + 1) % mod;
}
if(!A) res = 0;
cout << res << endl;
return 0;
}
公式法另解
除了分治法外,还可以用乘等比数列和公式得出分子,再乘分母逆元的做法,这里又涉及到了另外一个知识点
这样的话,就可以愉快地套用高中学的等比数列和公式来求每一项,但这里需要特判逆元不存在的情况
- 分母p-1是mod的倍数,不存在逆元,这时直接乘$(1+p+…p^k)\%mod$, 即 $k + 1$
- 分母p-1不是mod的倍数,存在逆元,这是需要用快速幂求分子,再用快速幂求分母的逆元,两者相乘就得到了每一项
AC代码
#include<iostream>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int mod = 9901;
int A, B;
//保存质因子以及出现的次数
unordered_map<int, int> primes;
//试除法质因子分解
void divide(int n) {
for(int i = 2; i <= n / i; i++) {
if(n % i == 0) {
while(n % i == 0) {
primes[i]++;
n /= i;
}
}
}
if(n > 1) {
primes[n]++;
}
}
//快速幂
int qmid(int a, int b) {
int res = 1;
while(b) {
if(b & 1) res = (LL)res * a % mod;
a = (LL)a * a % mod;
b >>= 1;
}
return res;
}
int main(){
cin >> A >> B;
//对A分解质因子
divide(A);
int res = 1;
for(auto it : primes) {
//p是质因子,k是质因子的次数
int p = it.first, k = it.second * B;
// res要乘上每一项, 注意这里是k + 1
if((p - 1) % mod == 0) {
//不存在逆元,由于p - 1的是mod 的倍数, 故p % mod = 1
//所以1 + p + ... + p^k每个数%mod 都是1,共k + 1个数,总就是k + 1
res = (LL)res * (k + 1) % mod;
}
else{
res = (LL)res * (qmid(p, k + 1) - 1) % mod * qmid(p - 1, mod - 2) % mod;
}
}
if(!A) res = 0;
cout << (res % mod + mod ) % mod << endl;
return 0;
}
对于这个题,我想了个稍微暴力一点点的做法,由于模数只有9901,所以一个x^i最多9901个就会出现重复,我们记录下来重复的位置,然后利用循环节就可以解决求sum函数的过程,算一个因子的复杂度最多最多是o(10000),不到20个因子,算起来也很快,不过只能针对于想不到这个函数,以及模数很小的情况
sum(p, k-1)为什么是k-1呢
想问一下,什么时候取模呢
请问公式法里为什么(p-1)%mod=0的时候还是算p的k+1次方-1就不对呢,按道理不是一样的吗
你这样乘的不就是 0 了吗?p 的 k+1 次方 %mod=1,减一就等于 0
怎么想到的呢???
头像太搞了hhhh
当p-1是mod的倍数时,不存在逆元,那么分母为什么可以直接等于1呢?
分母p-1是mod的倍数,不存在逆元,这时直接乘$(1+p+…p^k)\%mod$, 即 $k + 1$
为什么之后不再乘一个1/(p-1)%mod呢,
由乘法取模公式有:(x/y)%mod=(x%mod)(1/y)%mod。
不是应该有这个式子[(1+p+···p^k)/(p-1)]%Mod=(1+p+···p^k)%mod(1/(p-1))%Mod吗?
因为p-1是mod的倍数 $1+p+…+p^{k}$有k+1项,每项模p-1都得1,所以全部加起来是k+1, 然后再%mod就行了
我理解的和你的稍微不一样
好的好的,多谢解答,感谢。
当逆元不存在的时候,这个地方没有用到等比数列求和的方式,直接拿的原式去去做的
这个分母与mod不互质的处理实在是tql%%
orz orz orz orz orz orz
%%%
请问一下这里A为0时,为什么没有特判情况下得数不为0
这个特判是啥意思啊
如果A是0,res就等于0.里面“!”意为取非
这我知道,我想问为啥a=0,res就等于0
这里a有可能为0,0没有约数
按照代码不用特判最后出来不也是0吗
当时可能没有想到,直接判了,不都是一样的吗
ORZ
orz
我有一个比较傻的问题,分治的时候为什么要分情况讨论奇偶啊,有啥影响吗
除以二的时候如果是奇数 下取整的时候数对不上
懂了,这里分奇偶是为了降低时间复杂度,
如果只写偶数,那么跟你说的一样,奇数的时候不适用,
如果只写奇数,那么由于有些质数的个数会非常大,所以递归层数太深,内存超限
太妙了!
为啥sum每个都要加一???
因为sum本身的含义是一直加到p的k-1次方,所以通过加一算到p的k次方。
为什么分治法里的奇数是加p^(k-1)而不是加p^k呢
因为是k-1项
第一种方法的时间复杂度是多少啊,不太会算
T(k)=T(k/2)➕log(k)(快速幂的复杂度)
,T(1)=o(1)
递归logk次到1
所以就是上界就是
log(k)*log(k)
总复杂度就是质因子数✖️递归复杂度
那么求质因子的复杂度大概为多少
求出所有质因子复杂度是O(根号a),我上面有个说错了,最后结果计算复杂度是质因子数*递归复杂度,总的复杂度准确点就是再加个根号a
1
%%%
大佬
为什么有时候是负数呢? (res % mod + mod ) % mod 这一步是为了防止出现负数?
是因为出现了减法?
因为C++的%运算不是返回正数的,比如-5%3=-2, 然而我想要的是一个正数的%运算,所以最后就是(res % mod + mod ) % mod
有负数的情况是因为乘了逆元
理论上这个逆元是不会出现负数的吧,为什么最后结果有负数了呢,求解
快速幂的时候取了模可能为0,然后公式中 -1 就成负数了