算法题越狱

 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;
}

猜你喜欢

转载自blog.csdn.net/weixin_39910711/article/details/82531813