快速幂
用处
一个能快速求 n k % p n^{k}\%p nk%p的算法,在数论题目里可以用来模拟多次重复的运算,是数论的基础算法。
模板题
题目转载———caioj.cn
【题目描述】
求 n k % p n^{k}\%p nk%p的值。
【输入格式】
一行,包含三个数 n , k , p n,k,p n,k,p
【输出格式】
一行,输出 n k % p n^{k}\%p nk%p的值。
样例
样例输入
2 6 23
样例输出
18
样例说明
2 6 = 64 , 64 % 23 = 18 2^{6}=64,64\%23=18 26=64,64%23=18
数据范围与提示
对于全部数据, 1 ≤ n , k ≤ 1 0 15 , 1 ≤ p ≤ 1 0 9 1\le n,k\le 10^{15},1 \leq p \leq 10^{9} 1≤n,k≤1015,1≤p≤109
解析
这是一道基本的数论题,不用数论算法就会超时(简单来说,不用数论思想就是暴力模拟)。
暴力模拟的时间复杂度为
O
(
k
)
,
O(k),
O(k),但
k
k
k最大可以取
1
0
15
,
10^{15},
1015,肯定会超时。
快速幂是一种时间复杂度为
O
(
l
o
g
2
(
k
)
)
O(log2(k))
O(log2(k))的算法,这代表
k
k
k甚至可以取到
2
2
×
1
0
8
(
2
200000000
)
\LARGE2^{\normalsize2\times10^{8}}(2^{200000000})
22×108(2200000000)。
可以说是无限了,C++中很难有这样的时间和内存够这种数运算了
(
F
F
T
(FFT
(FFT也做不到呀
)
)
)。
为什么说快速幂时间复杂度为为
O
(
l
o
g
2
(
k
)
)
,
O(log2(k)),
O(log2(k)),这因为它的算法模式。
利用
2
2
2进制来分治,将运算次数缩小到二分的次数。
比如数据
2 29 657
n
=
2
,
k
=
29
,
p
=
657
n=2,k=29,p=657
n=2,k=29,p=657
将
k
k
k转化成
2
2
2进制得
11101
,
a
n
s
=
1
11101,ans=1
11101,ans=1
每次
n
n
n都会平方并进行取模,然后将
k
÷
2
k \div 2
k÷2
因为
n
k
=
(
n
×
2
)
k
÷
2
n^{k}=(n\times2)^{k \div 2}
nk=(n×2)k÷2
但是
C
C
C语言中默认向下取整,所以
5
÷
2
=
2
,
5\div 2=2,
5÷2=2,但是还有个
1
1
1该怎么办呢?
所以为什么转化为
2
2
2进制,当
k
%
2
=
1
,
k\%2=1,
k%2=1,代表有一个
1
1
1是多出来的,要先把这
1
1
1个
a
a
a先乘上。
具体代码实现如下
#include<cstdio>
#include<cstring>
using namespace std;
int n,k,p;
int ksm(int a,int b,int inf)
{
int ans=1;a%=inf;
while(b)
{
if(b&1/*b%2==1*/)ans=ans*a%inf;
a=a*a%inf;b>>=1;/*b=b/2;*/
}
return 0;
}
int main()
{
scanf("%d%d%d",&n,&k,&p);
printf("%d\n",ksm(n,k,p));
}
练习题 NOIP2013提高组day1【转圈游戏】
题目描述
n
n
n个小伙伴
(
(
(编号从
0
0
0 到
n
−
1
)
n-1)
n−1)围坐一圈玩游戏。按照顺时针方向给
n
n
n个位置编号,从
0
0
0到
n
−
1
n-1
n−1。最初,第
0
0
0号小伙伴在第
0
0
0号位置,第
1
1
1号小伙伴在第
1
1
1号位置
,
…
…
,
,……,
,……,依此类推。
游戏规则如下:每一轮第
0
0
0号位置上的小伙伴顺时针走到第
m
m
m号位置,第
1
1
1号位置小伙伴走到第
m
+
1
m+1
m+1号位置
,
…
…
,
,……,
,……,依此类推,第
n
−
m
n−m
n−m号位置上的小伙伴走到第
0
0
0号位置,第
n
−
m
+
1
n-m+1
n−m+1号位置上的小伙伴走到第
1
1
1号位置
,
…
…
,
,……,
,……,第
n
−
1
n-1
n−1号位置上的小伙伴顺时针走到第
m
−
1
m-1
m−1号位置。
现在,一共进行了
1
0
k
10^{k}
10k轮,请问
x
x
x号小伙伴最后走到了第几号位置。
输入格式
输入共 1 1 1行,包含 4 4 4个整数 n , m , k , x , n,m,k,x, n,m,k,x,每两个整数之间用一个空格隔开。
输出格式
输出共 1 1 1行,包含 1 1 1个整数,表示 1 0 k 10^{k} 10k轮后 x x x号小伙伴所在的位置编号。
样例
样例输入1
10 3 4 5
样例输出1
5
数据范围与提示
对于
30
%
30\%
30%的数据
,
0
<
k
<
7
;
,0 < k < 7;
,0<k<7;
对于
80
%
80\%
80%的数据
,
0
<
k
<
1
0
7
;
,0 < k < 10^{7};
,0<k<107;
对于
100
%
100\%
100%的数据
,
1
<
n
<
1
0
6
,
0
<
m
<
n
,
1
≤
x
≤
n
,
0
<
k
<
1
0
9
。
,1 < n < 10^{6},0 < m < n,1 \leq x \leq n,0 < k < 10^9。
,1<n<106,0<m<n,1≤x≤n,0<k<109。
题解
这道题不难可以推出
(
x
+
m
×
1
0
k
)
%
n
(x+m\times10^{k})\%n
(x+m×10k)%n
根据模运算的的分配率可以得到
(
x
%
n
+
m
%
n
×
1
0
k
%
n
)
%
n
(x\%n+m\%n\times10^{k}\%n)\%n
(x%n+m%n×10k%n)%n只需用快速幂求出
1
0
k
10^{k}
10k就可以了
A
C
AC
AC代码
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,k,x;
int ksm(int a,int b)
{
int ans=1;
while(b)
{
if(b&1)ans=ans*a%n;
a=a*a%n;b>>=1;
}
return ans;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&x);
printf("%d\n",(x%n+m%n*ksm(10,k)%n)%n);
return 0;
}