HDU 4704 Sum 隔板法 快速幂 乘法取模 费马小定理

传送门

首先把 N 拆成 N 个 1, 那么要分成 k 份的话,就在 N-1 个空隙里找出 k-1 个插进去就可以了。选法是 C(n-1, k-1) 这是高中组合数学学的隔板法。
而这题 k 的取值范围是 0~n-1
所以就是求 ΣC(n-1, k-1) (k从 0 到 n),那么就是求 2^(n-1)

接下来怎么求呢?脑子里一想,嗯,快速幂,对,快速幂。
但是这个幂次有 10^100000 啊,存都存不下啊,快速幂个锤子
这个时候就要请费马小定理出场了

假如 p 是质数,且 gcd(a,p) = 1,那么 a(p-1) ≡ 1(mod p)

哦豁,这里怎么用呢
你要求 2w % 1e9+7
显然 2 和 1e9+7 互质,且 1e9+7 是素数,那么 2(1e9+7-1) ≡ 1(mod p)
所以我们把 2w % 1e9+7拆成了 2^((1e9+7-1)*k + t)^ % 1e9+7
即 (2(1e9+7-1)*k %1e9+7)x(2t %1e9+7)
根据费马小定理前一部分为 1

所以现在的问题是求出 t 然后快速幂就可以了
怎么求 t 呢
t是 w mod (1e9+7-1) 的结果
我们用 string 读入 w
根据取模运算的运算法则我们知道
(10*m+n)%p = [(10*m)%p + n%p]%p
这样我们就可以从左到右依次对 string 进行处理得到 string % (1e9+7-1)
这样剩下的数就足够小使得我们可以用快速幂来解决了

#include 
const int mod = 1e9+7;
using ll = long long;
using namespace std;
ll p(ll x, ll n) {
	ll res = 1;
	while (n) {
		if (n & 1) {
			res *= x;
			res %= mod;
		}
		x *= x;
		x %= mod;
		n >>= 1;
	}
	return res % mod;
}
int main() {
	string ss;
	while (cin >> ss) {
		ll m = mod-1;
		ll tmp = 0;
		for (int i = 0; i < ss.size(); ++i) {
			tmp = (tmp*10 + (ss[i]-'0'))%m;
		}
		cout << p(2, tmp-1) << endl;
	}
	return 0;
}

你可能感兴趣的:(杂谈)