1. 程式人生 > >hdu 4704 sum(費馬小定理+快速冪)

hdu 4704 sum(費馬小定理+快速冪)

題意:

  這題意看了很久。。

    s(k)表示的是把n分成k個正整數的和,有多少種分法。

  例如:

n=4時,

s(1)=1     4

  s(2)=3     1,3      3,1       2,2

     s(3)=3     1,1,2         1,2,1       2,1,1

    s(4)=1       1,1,1,1

s(1)+s(2)+s(3)+s(4)=1+3+3+1=8

當n=1,2,3,4時,可以分別求出結果為    1,2,4,8

於是推出答案就是2^(n-1)---------------------(為什麼?我也不知道,這個是怎麼推出來的)

思路:題意明白了,就是求2^(n-1)    mod(10^9+7)唄。

由於這裡的n非常大,1<=n<10^100000,這裡用簡單的暴力絕對超時啊(10^8就會超時)

那麼,這裡用了兩個優化,一個是費馬小定理 a^(p-1)≡1(mod p),另外就是快速冪加速求冪

這裡稍微演示一下如何利用費馬小定理處理:



(單單用費馬小定理處理仍會超時,因為處理完後,指數最大能達到10^9級別,仍會超時)

程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int mod=1e9+7;

void pow(__int64 b){//快速求冪(位操作)
	__int64 a=2;
	__int64 ans;
	ans=1;
	while(b>0){
		if(b&1)//判斷是否為奇數,相當於 if(b%2==1)
			ans=(ans*a)%mod;
		a=(a*a)%mod;
		b=b>>1;//二進位制向右移一位,相當於 b=b/2;
	}
	printf("%I64d\n",ans);
}

int main(){
	char str[123456];
	__int64 num,i;
	while(~scanf("%s",str)){
		num=0;
		int len=strlen(str);
		for(i=0;i<len;i++){
			num=( num*10+(str[i]-'0') )% (mod-1);//精華部分
		}
		if(num==0) pow(mod-2);//此時n=mod-1,所以應該求pow(mod-2),而不能求成pow(num-1)
		else pow(num-1);
	}
	return 0;
}