HDU-4704

组合数学+费马小定理化简

首先是隔板的思想,s(i) 的意思可以解读为把n个木条排成一排,分成 i 份 ,那么我们根据隔板法,在n个木条就有n-1个空位可以加入隔板(因为每个数是正整数),那么s(i) = C( n-1 , i )

那么s(1) + s(2) + ... + s(n) = 2 ^ (n-1) 答案即为 2 ^ (n-1) % mod 

但是因为n特别大,可以用费马小定理进行优化

2 ^ n %  m =  2 ^ ( (n % (m-1) ) + [n / (m - 1)] * m-1 )  % m( 其中 [ ] 代表取整,因为一个数可以分解成 num = q * mod + p,)

那么同余定理可知, 2 ^ ( (n % (m-1) ) + [n / (m - 1)] * m-1 )  % m = (  ( 2 ^ (n % (m-1) ) ) % m * (2 ^ k) ^ (m-1) ) % m ) % m

因为m在这里是1e9 + 7 是一个质数,那么  (2 ^ k) ^ (m-1) ) % m = 1 

最终可以得到 2 ^ n %  m = 2 ^ (n % (m-1) ) % m

n % m-1 就在我们的可计算范围内了

那么我们先计算 n % m-1 然后通过快速幂计算2 ^ (n % (m - 1) ) % m

代码如下 :

           

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int maxn = 1e5 + 10;

const int mod = 1e9 + 7;

char s[maxn];

LL qpow(LL a,LL b,LL mod){

    LL ans = 1,base = a % mod;

    while(b){

        if (b & 1) ans = ans * base % mod;

        base =  base * base % mod;

        b >>= 1;

    }

    return a % mod;

}

int main(){

    while(scanf("%s",s) == 1){

        LL num = 0;

        int len = (int)strlen(s);

        for (int i=0; i<len; i++)

            num = (num*10 + s[i]-'0')%(mod-1);

        if (num == 0){

            printf("%lld\n",qpow(2, mod-2, mod));

        }else{

            printf("%lld\n",qpow(2, num-1, mod));

        }

    }

    return 0;

}

猜你喜欢

转载自blog.csdn.net/CCCCTong/article/details/82633856
hdu