越狱
监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种。如果
相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱
Input
输入两个整数M,N.1<=M<=10^8,1<=N<=10^12
Output
可能越狱的状态数,模100003取余
Sample Input
2 3
Sample Output
6
Hint
6种状态为(000)(001)(011)(100)(110)(111)
题目分析 :
1.n个房间,m个宗教,我们可以知道所有不同的状态为mn ,我们可以从彼此没有相邻的角度出发,第一个房间可以是任意一种宗教即m种,第二个房间只能是m-1种,那第三个房间呢?会根据其前面的宗教有影响吗?答案是没有的,你想,假设第一间为红色,第二间为黑色,第三间仍可以是红色,只是不为黑色而已。故彼此不相邻的宗教总数为m*(m-1)(n-1)。
2.到这里,这道题的核心部分来了。如果用pow的基本算法来做,n<=1012计算量太大,定然会超时.。所以我们需要更好的求幂的算法,所以这又是一个知识点。
3.解决了求幂的问题,我们还面临这数据特别大的问题,大数问题一般存在两种解决的情况,一种是建立大数的模板,自己重载四则运算。另一种,算是题目给你降低了难度——mod上一个数。但是摩一个数又涉及mod的运算法则,不过这是个简单的知识点,相信大家都明白。
理解和收获 : 1.快速求幂 2.mod运算法则。
先简单的阐述下——快速求幂吧!
例如 a^11=a^(2^0+2^1+2^3)。
我们发现11的二进制数为1011,可以看到,a^11化为a^(2^0+2^1+2^3)只需要三次乘法就可了(因式分解差成乘法),大大减少了运行的次数。
LL spow(LL x, LL n)
{
LL result = 1;
while (n > 0)
{
if (n & 1)//二进制上的数是否为1
result = ((result%maxn)*(x%maxn))%maxn;
x =((x%maxn)*(x%maxn))%maxn;
n >>= 1;//向二进制的下一位移动
}
return result%maxn;
}
这里我也简单的证明一下mod的乘法法则。
公式 :(n *(n+1) * (n+2) * ········ * (m-1) * m )% maxn = ((n %maxn)*((n+1)%manx) *( (n+2)%maxn) * ········ *( (m-1)%maxn) * m) %maxn
假设 (4*6 *2)%10 = 48%10 = 8
4%10 =4, 6%10 =6 , 2%10=2,
4*6 = 24 24%10 =4 , 4*2 = 8 ,最后一步就不能忘记 : 8%10 = 8
好了,这道题的难得部分也就全部解决了;
#include <algorithm>
#define maxn 100003
#include<iostream>
using namespace std;
typedef long long LL;
LL spow(LL x, LL n)
{
LL result = 1;
while (n > 0)
{
if (n & 1)
result = ((result%maxn)*(x%maxn))%maxn;
x =((x%maxn)*(x%maxn))%maxn;
n >>= 1;
}
return result%maxn;
}
int main()
{
LL m,n;
cin >> m >> n;
LL sum_can = spow(m,n);
LL sum_no = (m*spow(m-1,n-1))%maxn;
LL sum = (sum_can-sum_no)%maxn;
if(sum < 0)
sum += maxn;
cout << sum << endl;
}