题意分析
- 定义函数 ,然后在给定a和b的情况下,求的值。 简单明了。
思路分析
- 我们就按照题目的要求直接进行计算。但是我们发现这个题目我们在求幂次方的时候数字过大。而且很可能会爆。这里我们就要介绍快速幂的写法。快速幂就是让我们在对数的时间范围内快速计算幂次方的。这个思想有点类似于分治然后再进行归并的算法。
就比如上面的图,我们想要计算大的就先要计算小部分的,然后小部分的幂次方计算出来后就可以反过来推导出大的了。这一部分结合递归或者迭代就可以做到了。
快速幂的代码如下
ll qp(ll a,ll b){ ll c=1; while(b){ if(b&1) c=c*a%mod; b>>=1; a=a*a%mod; } return c; }
我们观察上面的代码,我们发现,在这个过程中我们出现了乘法,但是我们发现这个mod数还是挺大的。也就是可能会存在两个数字相乘爆long long的情况。所以为了避免这种情况,这里我们还需要介绍一直用快速乘的写法,其实和快速幂的思想是类似的。我们把其中一个因数看作一个二进制,所以我们就相当于一个数字与一个二进制的数字进行一个乘法。这样是不会爆的。
快速乘的写法如下
ll qc(ll a,ll b){ ll c=0; while(b){ if(b&1) c=(c+a)%mod; b>>=1; a=(a+a)%mod; } retrun c; }
我们可以观察到这两种写法是十分类似的。那么现在我们是否就可以解决这个题目了呢?我们发现,a和b的范围实在是太大了,所以我们如果直接暴力枚举的话很有可能会TLE。怎么办?我们继续观察,我们发现,这个序列其实就是一个等比数列的前n项和的一部分,我们可以把这个函数拆分成两个等比数列的差。我们令g(n)表示0-n的,公比为q的前n项的和。那么这个题目就可以表示为。我们直到等比序列的前n项和为,那么直接套进去就行了。但是我们发现这里有一个分母,而且这个模数是一个素数,所以我们可以直接使用费马小定理来求逆元。
这里大致分析一下逆元的求法。我们都知道,对于一个,如果m不能被a整除,那么这个就只能用分数进行表示吗?显然是否定的。那么我们可以使用费马小定理进行求解。让他表示为一个整数的形式。但是用费马小定理的前提是我们这个模数必须是一个素数。费马小定理就是。这样,我们就可以对本题进行求解了。
解法一 迭代版本
- 代码如下
- 快速幂和快速乘的时间复杂度都是级别的。然后总的时间复杂度就是
- 只开了少数几个变量来求。空间复杂度为
class Solution { public: /** * * @param a int整型 * @param b int整型 * @param n int整型 * @return long长整型 */ typedef long long ll; const ll mod = 10000000033; // 快速幂的写法,计算a^b%mod的值 ll qp(ll a,ll b){ ll c=1; while(b>0){ if(b&1) c=qc(a,c)%mod; b>>=1; a=qc(a,a)%mod; } return c; } // 快速乘的写法,计算a*b%mod的值 ll qc(ll a,ll b){ ll ans=0; while(b){ if(b&1){ ans=(ans+a)%mod; } b>>=1; a=(a+a)%mod; } return ans; } long long solve(int a, int b, int n) { // write code here if(n==0){ return 0; }else if(n==1){ return b-a+1; }else{ // 利用等比数列的前n项和的计算公式 ll up=(qp(n,b+1)-qp(n,a)+mod)%mod; // 逆元操作 ll down=qp((ll)n-1,mod-2); down=(down%mod+mod)%mod; return (qc(up,down)+mod)%mod; } } };
解法二 递归版本
- 代码如下
- 快速幂和快速乘的时间复杂度都是log级别的。然后总的时间复杂度就是
- 只开了少数几个变量来求。空间复杂度为
class Solution { public: /** * * @param a int整型 * @param b int整型 * @param n int整型 * @return long长整型 */ typedef long long ll; const ll mod=10000000033; // 快速幂的写法,计算a^b%mod的值 ll qp(ll a,ll b){ if(b==0) return 1; if(b&1) return qc(a,qp(a,b-1))%mod; else{ ll mul=qp(a,b/2); return qc(mul,mul)%mod; } } // 快速乘的写法,计算a*b%mod的值 ll qc(ll a,ll b){ if(b==0) return 0; if(b&1) return (a+qc(a,b-1))%mod; else{ ll tmp=qc(a,b/2); return (tmp+tmp)%mod; } } long long solve(int a, int b, int n) { // write code here if(n==0){ return 0; }else if(n==1){ return b-a+1; } // 利用等比数列的前n项和的计算公式 ll up=(qp(n,b+1)-qp(n,a)+mod)%mod; // 逆元操作 ll down=qp((ll)n-1,mod-2); down=(down%mod+mod)%mod; return (qc(up,down)+mod)%mod; } };