[BZOJ1008][HNOI2008]越狱

来源:互联网 发布:淘宝首页源代码下载 编辑:程序博客网 时间:2024/06/05 11:43

提交地址
Description

监狱有连续编号为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)

思路:
我们弄一个抽象的题意:
有n个盘子,m种荔枝。
每个盘子放一个荔枝,相邻的盘子荔枝种类一样,求方案数。
1<=M<=10^8,1<=N<=10^12
答案模100003

第一个盘子能放m种荔枝,那么第二个盘子呢???
为了不与第一个盘子的荔枝重复,第二个盘子只能放m-1种荔枝。
第三个盘子呢?
为了不与第二个盘子的荔枝重复,第三个盘子也是放m-1种荔枝?
求可能搭配出的方案数。
这不就是小学奥数上的乘法原理嘛!!!
于是我们得出
num=m * (m-1)^(n-1)
这是合法的数量。
总的数量?
all=m^n (这个一眼就看出来了)
ans=all-num
嗯。那直接乘?但很尴尬的是,这个东东太大了1<=M<=10^8,1<=N<=10^12,明显过不了。
在求 a^n 前
我们可以先求出 a^(n/2)
然后 a^(n/2) * a^(n/2) => a^n
这样一直分割下去,我们能得到一个
O(log(n))的算法,名叫快速幂。
代码如下

#include<cstdio>#include<cstring>using namespace std;typedef long long LL;LL q_mod(LL a,LL b,LL c){    LL ans=1%c;a%=c;    while(b)    {        if(b&1)ans=ans*a%100003;        a=a*a%100003;b>>=1;    }    return ans;}int main(){    LL m,n;    scanf("%lld%lld",&m,&n);    LL s=((q_mod(m,n,LL(100003))-m*q_mod(m-1,n-1,LL(100003))%LL(100003)+LL(100003))%LL(100003));    printf("%lld\n",s);    return 0;}
0 0