模取幂和模取乘
说在前面
数论复习 Part 1。
FLG的顺序是可以换的,从自己只会皮毛的东西整收益最大。
(指自己咕咕咕了自己更差的DP)
瞎写吧,随便拾遗。
模取幂
我以前也写过一篇模取幂,写的是(儒雅随和),不用去看了。
分析
现在我们要求
a
b
(
m
o
d
c
)
a^b\pmod{c}
ab(modc),
Θ
(
b
)
\Theta(b)
Θ(b)的时间复杂度不行。
我们知道
a
p
+
q
=
a
p
∗
a
q
a^{p+q}=a^p*a^q
ap+q=ap∗aq,于是我们把
b
b
b二进制一下,比方说
设
a
n
s
=
3
19
(
m
o
d
8
)
设ans=3^{19}\pmod 8
设ans=319(mod8),
(
19
)
10
=
(
10011
)
2
(19)_{10}=(10011)_2
(19)10=(10011)2,则
a
n
s
=
3
2
0
∗
3
2
1
∗
3
2
4
(
m
o
d
8
)
ans=3^{2^0}*3^{2^1}*3^{2^4}\pmod 8
ans=320∗321∗324(mod8)。
结束了。
如果还要加几句的话,
3
2
i
=
(
3
2
i
−
1
)
2
3^{2^i}=({3^{2^{i-1}}})^2
32i=(32i−1)2。
代码
int ksm(int a,int b,int c)
{
int ans=1;
while(b)
{
if(b&1) (ans*=a)%=c;
(a*=a)%=c,b>>=1;
}
}
模取乘
分析
类似的,我们更知道
a
∗
(
p
+
q
)
=
a
∗
p
+
a
∗
q
a*(p+q)=a*p+a*q
a∗(p+q)=a∗p+a∗q,于是我们二进制分解……
不过这次是把
Θ
(
1
)
\Theta(1)
Θ(1)劣化到
Θ
(
l
o
g
2
n
)
\Theta(log_2n)
Θ(log2n)级别,这是在乘法会爆的情况才采用的不得已的方法。
代码
int mul(int a,int b,int c)
{
int ans=0;
while(b)
{
if(b&1) ans=(ans+a)%c;
b>>=1; a=(a<<1)%c;
}
return ans;
}
Θ ( 1 ) \Theta(1) Θ(1)模取乘
分析
我们把两个
1
0
18
{10^{18}}
1018的数乘起来会发生什么?会炸掉?会循环,
9223372036854775807
+
x
会
变
成
−
9223372036854775808
+
x
−
1
9223372036854775807+x会变成-9223372036854775808+x-1
9223372036854775807+x会变成−9223372036854775808+x−1。你做个试验就会发现了。
怎么规避负数然后取模呢?
这样,
−
9223372036854775808
+
a
∗
b
−
1
−
(
−
9223372036854775808
+
⌊
a
∗
b
c
⌋
×
c
−
1
)
=
a
∗
b
−
⌊
a
∗
b
c
⌋
×
c
=
a
∗
b
m
o
d
c
-9223372036854775808+a*b-1-(-9223372036854775808+\lfloor \frac{a*b}{c}\rfloor \times c-1)=a*b-\lfloor \frac{a*b}{c}\rfloor \times c=a*b\mod{c}
−9223372036854775808+a∗b−1−(−9223372036854775808+⌊ca∗b⌋×c−1)=a∗b−⌊ca∗b⌋×c=a∗bmodc。
如果long double也救不了你的话就算了。
代码
LL mul(LL a,LL b,LL c)
{
return ((a*b-(LL)((long double)a/c*b+1e-8)*c)%c+c)%c;
}
Master Yi的惨痛教训教会我们,以下写法更不容易挂掉。
LL mul(LL a,LL b,LL c)
{
a%=c,b%=c;
return ((a*b-(LL)((long double)a/c*b+1e-8)*c)%c+c)%c;
}