逆元的定义:
当 a m ≡ 1(mod p), m即为 a 在mod p 意义下的逆元。(需要注意只有a和p互质,a才有关于p的逆元)。
公式推导:
a * b % p ≡ a * m % p;(两边同时乘以b,再除以a)。
1 % p ≡ m * b % p(m为满足这个条件的最小的一一个数,我们就将其称之为b的逆元)。
费马小定理:
由费马小定理可知:对于任意的质数 p ,任意a都满足 ≡ a,化简可得:b * ≡ b。与前面的逆元公式一一对应可得,m == 。
为什么除法要用逆元?
a * b % p == ( ( a % p ) * ( b % p ) ) % p
a / b % p != ( ( a % p ) / ( b % p ) ) % p
在除法运算中不可以对分子分母分别取模来进行计算,当 a 和 b 足够大时如果强行分别取模的话会导致精度缺失较大,结果就不准确了。
例题:牛客--小y的树
题目描述:
求一颗 n 层的满 k 叉树,求任意两点之间距离和等于多少,答案对取模。
树上两点距离: 沿着最短路径从 u 走到 v 经过的边的数量。
n 层的满 k 叉树:叶子节点到根的距离是 n − 1,每个点都有 k 个儿子。
输入描述:
一行两个正整数 n, k(2 <= n, k <= )。
输出描述:
输出一行一个整数代表答案。
示例:
3 2
48
思路:
我们算总共走过的边的数量,换而言之就是要我们算每条边会被走多少次,每次我们可以以一条边v为分割线将树分成两个部分A, B分别有a,b 个点,计算从A(B)出发走完所有的B (A) 的点会经过多少次 v (很显然 a * b 次),然后计算v那一条边同一层会有多少条边 V,故V层会贡献 a * b * V 次。(注意v和V的区别)
这个题的点的计算方法我知道的有两种:
1.递推(这种这里不作详细解释)
2.利用等比数列求和公式,这里涉及到乘法快速幂求逆元了。
- 首先给出求和公式 : a1 * (1 - ) / (1 - q) (mod p)。
- 用逆元除法变乘法 :a1 * (1 - ) * (mod p)。
- 还有一个需要注意的一点,取模情况一旦涉及减法就一定要先加上模数再进行取模操作
AC代码:
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 1e6 + 7, mod = 1e9 + 7;
ll ksm(ll a,ll b,ll p)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % p;
a = a * a % p;
b >>= 1;
}
return ans;
}
void solve()
{
ll n, k;
ll ans = 0, sum, x = 1, y1, y2;
cin >> n >> k;
sum = -1 * (1 - ksm( k, n, mod)) * ksm( k - 1, mod - 2, mod) % mod;//求逆元
for(int i = 1;i < n;i ++)
{
x = x * k % mod;
y1 = -1 * (1 - ksm(k,n - i, mod)) * ksm( k - 1, mod - 2, mod) % mod;
y2 = ((sum - y1) % mod + mod) % mod;
ans = (ans + (((y1 * y2) % mod) * x) % mod) % mod ;
}
cout << ans <<endl;
}
int main()
{
solve();
return 0;
}