快速幂+等比数列前n项求和并取模+求逆元

题目链接:https://acm.zzuli.edu.cn/zzuliacm/problem.php?cid=1242&pid=4

解法:题中给是求一个等比数列的前n项和取模(1e9+7),已知等比数列前n项求和公式为\(s=\frac{a_1(q^n-1)}{q-1}\),现在求前n+1项即可,q==1的时候特判断。但是比较麻烦的是需要取模,也就是我们要求的结果实际是\(s=\frac{q^{n+1}-1}{q-1}\ mod\ (1e9 + 7)\),注意我们求\(q^{n+1} - 1\)时是使用的快速幂算法,已经取过模了,如果与\(q-1\)相除再求模结果会错,所以我们要把\(\frac{x}{y}\ mod\ p\)的形式改为等价的\(x*z\ mod\ p\)的形式。

替换的方法:(参考于http://blog.csdn.net/mengxiang000000/article/details/53468865)

逆元的定义:若有\(A*x\equiv 1(mod\ y)\)其中A是最小满足这个式子的数,则A就是x对y的逆元。
如果有:
\(s = \frac{x}{y} \ mod\ p\),那么我们就可已寻找y对p的逆元,使得出发变成乘法;若此时设定z为y的逆元,则有\(s\equiv \frac{x}{y} * (y*z) \ mod\ p\),即\(s\equiv x*y\ mod\ p\)

参考代码:

注意是参考代码,不一定可以AC,应该会有一些细节有问题

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long LL;

const LL MOD = 1000000007;

LL mod_pow(LL x, LL n){
    LL res = 1;
    while (n > 0){
        if (n & 1) res = res * x % MOD;
        x = x * x % MOD;
        n >>= 1;
    }
    return res;
}

LL exgcd(LL m, LL n, LL &x, LL &y){
    LL x1, y1, x0, y0;
    x0 = 1; y0 = 0;
    x1 = 0; y1 = 1;
    LL r = (m % n + n) % n;
    LL q = (m - r) / n;
    x = 0; y = 1;
    while(r){
        x = x0 - q * x1; y = y0 - q * y1; x0 = x1 ;y0 = y1;
        x1 = x; y1 = y;
        m = n; n = r; r = m % n;
        q = (m - r) / n;
    }
    return n;
}

LL cal(LL a, LL m){
    LL x, y;
    LL gcd = exgcd(a, m, x, y);
    m = m < 0 ? -m : m;
    x = (x % m + m) % m;
    return x;
}

int main(){
	LL cs = 0;
	LL k, n;
	while (~scanf("%lld%lld", &k, &n)){
		if (k == 1){
			printf("Case %lld: %lld\n", ++cs, (n + 1) % MOD);
		}
		else{
			//求逆元
			LL z = cal(k - 1, MOD);
			//注意这里求x的方式,没有直接用q^(n+1)-1
			LL x = mod_pow(k, n + 1);
			if (x == 0){
				x = MOD - 1;
			}
			else{
				x = x - 1;
			}
			LL res = x * z % MOD;
			printf("Case %lld: %lld\n", ++cs, res);
		}
	}
	return 0;
}
posted @ 2017-08-18 22:06  LuSimon  阅读(596)  评论(0编辑  收藏  举报