越狱

  监狱有连续编号为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个宗教,我们可以知道所有不同的状态为m,我们可以从彼此没有相邻的角度出发,第一个房间可以是任意一种宗教即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;
}

 

posted @ 2017-08-02 19:13  Hunter丶安  阅读(204)  评论(0编辑  收藏  举报