1.快速求幂 2.mod运算法则。
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)
Source
正面考虑此题有点难,但从反面考虑就容易不少了,可以先求所有犯人互不冲突的方案数,第一个犯人有m种可能的宗教,为了不和第二个犯人冲突,第二个犯人有m-1种可能的宗教,第三个犯人也有m-1种可能的宗教。。。以此类推,方案数为m*[(m-1)^(n-1)],所有犯人的搭配方案总数为m^n,则结果为m^n-m*[(m-1)^(n-1)],由于题目数据范围太大,加之有输出mod后的结果的限制,用快速幂就OK
C
#include <stdio.h>
#define MOD 100003
#define LONG long long int
//结果=m^n-m*[(m-1)^(n-1)]
LONG pow(LONG x,LONG base) //快速幂,求(base^x)%MOD
{
if(x==0) return 1;
LONG tmp=pow(x/2,base);
tmp*=tmp;
tmp%=MOD;
if(x&1) //x是奇数,则还要乘一次底数
{
tmp*=base;
tmp%=MOD;
}
return tmp;
}
int main()
{
LONG n,m,compSet,uniSet,ans; //compSet=补集,uniSet=全集,ans=结果
scanf("%lld%lld",&m,&n);
compSet=pow(n-1,m-1);
uniSet=pow(n,m);
compSet*=m;
compSet%=MOD;
ans=uniSet-compSet;
if(ans<0) ans+=MOD;
printf("%lld\n",ans);
return 0;
}
(1)if(n&1)
&是位与操作符,n&1,不是将n的二进制形式与00000000 00000001按位做与操作。这时,只要n的最右边一位是1,结果就不是0,为true,条件成立。
所以这句话实际上就是if(n%2==1)
(2)快速幂取模
https://blog.csdn.net/DBC_121/article/details/77646508
快速幂取模的思路:
引理:积的取余等于取余的积的取余。
再在这条引理的基础之上,对指数型数据进行拆分以及合并,从而得到我们用的快速幂算法。
对于普通类型的求a^n,我们的求法是a*a*a*a....,这样乘以n次,时间复杂度为O(n)。对于普通n比较小的我们可以接受,然而当n比较大的时候,计算就慢了,所以我们就去寻找更快捷的计算方法!
例如:我们要求2^8,我们通过当为偶数的时候,a^n=(a*a)^(n/2),当n为奇数时,a^n=a*(a*a)^(n/2)的形式,是不是可以转化为4^4->8^2->64^1,就可以了,2^5的话2*4^2->2*16^1。下面直接给出代码吧。
① 降低a的规模:
//①a是底数,b是指数,mode是取模数,sum是记录取模的结果
int sum = 1;
a = a % mode;
for(int i = 1; i <= b; i++)
{
sum = sum * a;
}
sum = sum % c;
然还可以继续降低规模,即将循环中的那句换成sum = (sum * a)%mode
② 降低b的规模
1)先看b为偶数的样例
7^16,mode = 3,我们要怎么进行拆分?
最基本的拆分是这样的:7*7*7*7*7*7*7*7*7......7,上面的算法只是将其变为2*2*2*2*2*2*2......2,那么怎么减少b
的规模呢?你应该有一点感觉了吧。就是两两合并,将(7^16)变成(49^8),这就降低了b的规模,再利用上面
的算法降低a的规模,最终会变成1 *1*1*1*1*1*1*1。是不是感觉整个数简单了很多。
按照这个思路,我们可以多循环几次,从而不断的降低a和b的规模。
2)b为奇数
其实也很简单,我们只要在偶数算法的基础之上,每次把多出来的这个数跳出来,预先取模再带入即可。
long long Mode(long long a, long long b, long long mode)
{
long long sum = 1;
a = a % mode;
while (b > 0) {
if (b % 2 == 1) //判断是否是奇数,是奇数的话将多出来的数事先乘如sum
sum = (sum * a) % mode;
b /= 2;
a = (a * a) % mode;// 不断的两两合并再取模,减小a和b的规模
}
return sum;
}
用&操作符对b的奇偶性进行判断,附上用&实现的代码:
long long Mode(long long a, long long b, long long mode)
{
long long sum = 1;
while (b) {
if (b & 1) {
sum = (sum * a) % mode;
b--;
}
b /= 2;
a = a * a % mode;
}
return sum;
}