普通生成函数(OGF)
Contents
介绍
生成函数可以解决形如 满足XX条件的方案数共有多少种 的问题,它也能够解决 推导通项公式 等问题,生成函数常常与多项式运算结合在一起。
定义
对于一个无限数列
$$a = \{a_0, a_1, a_2, …\}$$
它的生成函数为
$$f(x) = a_0 + a_1x + a_2x^2 + … = \sum\limits_{i=0}^{\infty}a_ix^i$$
其中 $x$ 的值并没有意义。
• 有限数列 $\{a_0, a_1, a_2, …, a_n\}$ 的生成函数就是 $f(x) = a_0 + a_1x + a_2x^2 + … + a_nx^n = \sum\limits_{i=0}^{n}a_ix^i$
• 我们定义 $[x^{k}]f(x)$ 为 $f(x)$ 表达式中,$x^k$ 的系数。(为了方便表达,有的时候也会使用 $a_k, b_k$)
封闭形式与展开形式
生成函数有 封闭形式 和 展开形式 两种形态。
封闭形式适合进行生成函数之间的运算,展开形式则可以得到生成函数各项的系数(通项公式),从而获得最终的答案(方案数的统计)。
例如,对于无限数列
$$\{1,1,1,1,…\}$$
它的生成函数展开形式为:
$$f(x) = 1+x+x^2+x^3+…$$
很明显这是一个等比数列,公比为 $x$,进行求和可以得到封闭形式:
$$f(x) = 1+x+x^2+x^3+… = \frac{1}{1-x}$$
一些常见的封闭式与展开式的对应
封闭形式 | 展开形式 | 数列 | 通项公式 |
---|---|---|---|
$\frac{1}{1-x}$ | $\sum\limits_{i=0}^{\infty}x^i = 1+x+x^2+x^3+…$ | $\{1,1,1,1,…\}$ | $b_k = 1$ |
$\frac{1}{(1-x)^2}$ | $\sum\limits_{i=0}^{\infty}(i+1)x^i = 1+2x+3x^2+4x^3+…$ | $\{1,2,3,4,…\}$ | $b_k = k+1$ |
$\frac{1}{1-ax}$ | $\sum\limits_{i=0}^{\infty}a^ix^i = 1+ax+a^2x^2+a^3x^3+…$ | $\{1,a,a^2,a^3,…\}$ | $b_k = a^{k}$ |
$\frac{1}{(1-ax)^n}$ | $\sum\limits_{i=0}^{\infty}C_{(n-1)+i}^{(n-1)}a^ix^i$ | $\{1, C_{n}^{1}a^1, C_{n+1}^{2}a^2,C_{n+2}^{3}a^3,…\}$ | $b_k = C_{n+k-1}^{k}a^k$ |
$\frac{1}{1+x}$ | $\sum\limits_{i=0}^{\infty}(-1)^ix^i = 1-x+x^2-x^3+…$ | $\{1,-1,1,-1,…\}$ | $b_k = -1^k$ |
证明
证明1
$$\frac{1}{1-x} = 1+x+x^2+x^3+…$$
证:等比数列,公比为 $x$。
证明2
$$\frac{1}{(1-x)^2} = 1+2x+3x^2+4x^3+…$$
证:因为
$$\frac{1}{(1-x)^2} = (\frac{1}{1-x})^2 = (1+x+x^2+x^3+…)^2$$
根据组合意义,对于 $x^k$,有 $(0,k),(1,k-1),…,(k,0)$ 共 $(k+1)$ 种组合方式,并且每一种的系数为 $1$,所以 $x^k$ 的系数为 $(k+1)$。
证明3
$$\frac{1}{1-ax} = 1+ax+a^2x^2+a^3x^3+…$$
证:等比数列,公比为 $ax$。
证明4
$$\frac{1}{(1-ax)^n} = \sum\limits_{i=0}^{\infty}C_{(n-1)+i}^{(n-1)}a^ix^i$$
证:因为
$$\frac{1}{(1-ax)^n} = (1+ax+a^2x^2+a^3x^3+…)^n$$
根据组合意义,对于 $x^k$ 而言,每个 $(1+ax+a^2x^2+a^3x^3+…)$ 都可以贡献给 $k$,而这样的式子有 $n$ 个。
所以相当于:
求 $a_1+a_2+…+a_n = k$ 的非负整数解的个数。
这是 小球放盒子 模型,$k$ 个球放入 $n$ 个盒子并且允许空盒,用隔板法即可,方案数为 $C_{(n-1)+k}^{(n-1)}$,这就是 $a^kx^k$ 的系数。
证明5
$$\frac{1}{1+x} = 1-x+x^2-x^3+…$$
等比数列,公比为 $-x$。
计算例子
例1
题意
求无限数列 $a_i = i^2$ 的生成函数。
分别求出展开式和封闭式。
展开式 $\Rightarrow$ 封闭式
数列 $a$ 为:
$$a = \{0,1,4,9,16,…\}$$
那么生成函数的展开式为:
$$f(x) = x + 4x^2 + 9x^3 + 16x^4 + … = \sum\limits_{i=1}^{\infty}i^2x^i$$
在推导生成函数的封闭式时,一个非常重要的 trick 是给展开式 乘上一个 $x$。
那么乘上 $x$ 以后,相当于整个展开式向右移动了一位,对应的生成函数就是
$$xf(x) = 0 + x^2 + 4x^3 + 9x^4 + … = \sum\limits_{i=1}^{\infty}(i-1)^2x^i$$
所以两者相减,可得:
$$(1-x)f(x) = \sum\limits_{i=1}^{\infty}(i^2 - (i-1)^2)x^i = \sum\limits_{i=1}^{\infty}(2i-1)x^i$$
$$=2\sum\limits_{i=1}^{\infty}ix^i - \sum\limits_{i=1}^{\infty}x^i$$
$$=\frac{2x}{(1-x)^2} - \frac{x}{1-x}$$
• 注意到因为求和是从 $i=1$ 开始的,相当于 $i=0$ 的情况整体右移了一位,所以要乘上 $x$ 得到 $\frac{2x}{(1-x)^2}$
所以生成函数 $f(x)$ 的封闭式为:
$$f(x) = \frac{2x}{(1-x)^3} - \frac{x}{(1-x)^2} = \frac{x(x+1)}{(1-x)^3}$$
封闭式 $\Rightarrow$ 展开式
已知 $f(x)$ 的封闭式为
$$f(x) = \frac{x(x+1)}{(1-x)^3}$$
我们将其化简为上面 常用表 里面的组合,也就是 裂项:
$$f(x) = \frac{x(x+1)}{(1-x)^3} = \frac{2x}{(1-x)^3} - \frac{x}{(1-x)^2}$$
考虑第 $k$ 项的系数,注意到我们可以将分子中的 $x$ 提出来,所以 $f(x)$ 的第 $k$ 项系数 $a_k$ 就等于 $g(x) = (\frac{2}{(1-x)^3} - \frac{1}{(1-x)^2})$ 的第 $(k-1)$ 项系数 $b_{k-1}$。
$$\frac{2}{(1-x)^3} = 2\sum\limits_{i=0}^{\infty}C_{2+i}^{2}x^i, ~ \frac{x}{(1-x)^2} = \sum\limits_{i=0}^{\infty}(i+1)x^i$$
所以 $g(x)$ 的第 $(k-1)$ 项系数为:
$$b_{k-1} = [x^{k-1}]2(\sum\limits_{i=0}^{\infty}C_{2+i}^{2}x^i) - \sum\limits_{i=0}^{\infty}(i+1)x^i = 2C_{k+1}^2 - k = k^2$$
所以 $f(x)$ 的第 $k$ 项系数为 $a_k = b_{k-1} = k^2$。
于是生成函数的展开式为:
$$f(x) = \sum\limits_{i=0}^{\infty}i^2x^i = x + 4x^2 + 9x^3 + 16x^4 + …$$
例2
题意
求斐波那契数列 $$f_0 = 0, f_1 = 1, f_i = f_{i-1} + f_{i-2}$$
的生成函数,分别求出展开式(通项公式)和封闭式。
封闭式
注意到
$$f(x) = f_0 + f_1x^1 + f_2x^2 + f_3x^3 + …$$
$$xf(x) = 0 + f_0x^1 + f_1x^2 + f_2x^3 + …$$
$$x^2f(x) = 0 + 0+ f_0x^2 + f_1x^3 + …$$
由于 $f_i = f_{i-1} + f_{i-2}$,所以:
$$f(x)(1-x-x^2) = f_0 + (f_1-f_0)x^1 = x$$
所以 $$f(x) = \frac{x}{1-x-x^2}$$
封闭式$\Rightarrow$展开式
$$f(x) = \frac{x}{1-x-x^2}$$
我们希望将它转为表格中存在的形式,所以我们设:
$$(1-ax)(1-bx) = 1-x-x^2$$
可得 $a = \frac{1 + \sqrt 5}{2}, b = \frac{1 - \sqrt 5}{2}$
设
$$g(x) = \frac{f(x)}{x} = \frac{1}{(1-ax)(1-bx)}$$
那么要求 $f(x)$ 的第 $n$ 项系数,只要求 $g(x)$ 的第 $(n-1)$ 项系数即可。
因为
$$g(x) = \frac{1}{(1-ax)(1-bx)}$$ $$= \sum\limits_{i=0}^{\infty}a^ix^i * \sum\limits_{j=0}^{\infty}b^jx^j$$ $$= (1+ax+a^2x^2+a^3x^3+…) * (1+bx+b^2x^2+b^3x^3+…)$$
这是个卷积,那么 $g(x)$ 的第 $(n-1)$ 项系数为:
$$[x^{n-1}]g(x) = \sum\limits_{k=0}^{n-1}a^{k}b^{n-1-k}$$
$$=b^{n-1}\sum\limits_{k=0}^{n-1}a^{k}b^{-k}$$
$$=b^{n-1}\sum\limits_{k=0}^{n-1}(\frac{a}{b})^k$$
右边就是个等比数列求和了,公比为 $\frac{a}{b}$,最后可得
$$[x^n]f(x) = [x^{n-1}]g(x) = \frac{1}{\sqrt 5}((\frac{1+\sqrt 5}{2})^n - (\frac{1-\sqrt 5}{2})^n)$$
那么求出了 $f(x)$ 的通项公式,展开式也就可以写出来了。
例题
例1 洛谷P4721【模板】分治 FFT
题意
给定 $n-1$ 个非负整数 $g_1, g_2, … g_{n-1}$。
定义 $$f_0 = 1, f_i = \sum\limits_{j=1}^if_{i-j}g_j$$
求出 $f_1, f_2, …, f_{n-1}$,答案对 $998244353$ 取模。
其中,$2 \leq n \leq 10^5$
题解
看到 $\sum\limits_{j=1}^if_{i-j}g_j$ 就想到卷积,也就是多项式乘法。
那么令
$$f(x) = f_0 + f_1x^1 + f_x2x^2 + … + f_{n-1}x^{n-1}$$
$$g(x) = g_0 + g_1x^1 + g_x2x^2 + … + g_{n-1}x^{n-1}$$
$$h(x) = f(x)g(x)$$
则有:
$$[x^n]h(x) = \sum\limits_{i=0}^nf_ig_{n-i}$$
发现和 $f_n = \sum\limits_{j=1}^nf_{n-j}g_j$ 就差一个 $j=0$ 的情况(多了一个 $g_0f_n$),那么我们直接令 $g_0 = 0$ 就没问题了。
这样的话,当 $n=0$ 时,$h_0 = 0$,其他时候 $h_n = f_n$。
所以:
$$f(x)g(x) = h(x) = 0 + f_1x^1 + f_x2x^2 + … + f_{n-1}x^{n-1} = f(x) - f_0 = f(x) - 1$$
得到
$$f(x)(1-g(x)) = 1$$
$$f(x) = \frac{1}{1-g(x)}$$
多项式求逆即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
const int maxn = (1<<22) + 5;
// 板子省略
ll f[maxn], g[maxn], invG[maxn];
int main() {
int n; cin >> n;
for (int i = 1; i <= n-1; i++) cin >> g[i];
for (int i = 1; i <= n-1; i++) g[i] = (-g[i] + mod) % mod;
g[0] = 1;
poly_inverse(g, invG, n);
for (int i = 0; i < n; i++) {
cout << (invG[i]) % mod << " ";
}
cout << endl;
}
注意点
多项式的题目中,要注意几个点:
- 看到 $\sum_{i=0}^n f_ig_{n-i}$ 这种形式,即系数之和无论 $i$ 为多少,均为定值的,就想到 卷积,从而想到 多项式乘法。
- 转化出来的新多项式要注意 常数项。也就是代入 $n=0$ 判断一下左右两边是否相等。
例2 洛谷P4841 集训队作业2013 城市规划
题意
求 $n$ 个节点的 简单(无重边无自环)有标号无向 连通 图数目。答案对 $1004535809$ 取模。
其中,$n \leq 130000$
题解
小tips:计数问题可以想想
dp
怎么做。
设 $f_n$ 为 $n$ 个节点的 简单(无重边无自环)有标号无向 连通 图数目。
设 $g_n$ 为 $n$ 个节点的 简单(无重边无自环)有标号无向图数目。(区别在于不需要连通)
发现在一个完全图中有 $C_n^2$ 条边,每条边可以选或者不选,那么
$$g_n = 2^{C_n^2}$$
我们想要用另外一种方式表示 $g_n$,可以枚举 节点 $1$ 所在的联通块的大小:
- 节点 $1$ 所在联通块大小为 $1$ 时,方案数为:$f_1g_{n-1}$
- 节点 $1$ 所在联通块大小为 $2$ 时,方案数为:$C_{n-1}^{1}f_2g_{n-2}$
- 节点 $1$ 所在联通块大小为 $k$ 时,方案数为:$C_{n-1}^{k-1}f_kg_{n-k}$
所以联通块大小 $1$ 枚举到 $n$,有:
$$g_n = \sum\limits_{i=1}^n C_{n-1}^{i-1}f_ig_{n-i}$$
看到 $f_ig_{n-i}$ 的形式马上想到 卷积,也就是 多项式乘法。
但是有一个 $C_{n-1}^{i-1}$,我们把它拆开即可:
$$g_n = \sum\limits_{i=1}^n C_{n-1}^{i-1}f_ig_{n-i}$$
$$g_n = (n-1)!\sum\limits_{i=1}^n\frac{f_i}{(i-1)!}\frac{g_{n-i}}{(n-i)!}$$
令
$$F_i = \frac{f_i}{(i-1)!}, G_i = \frac{g_i}{i!}$$
则有:
$$\frac{g_n}{(n-1)!} = \sum\limits_{i=1}^nF_iG_{n-i}$$
令
$$F(x) = 0 + F_1x + F_2x^2 + … + F_{n}x^n$$
$$G(x) = G_0 + G_1x + G_2x^2 + … + G_{n}x^n$$
则可以发现,(注意 $F(x)$ 的常数项为 $0$):
$$[x^n]\frac{g(x)}{(n-1)!} = [x^n]F(x)G(x)$$
注意到 $\frac{1}{(n-1)!}$ 是不可以提到外面去的,因为它与 $n$ 有关。
定义
$$H_n = \frac{g_n}{(n-1)!}, H_0 = 0$$
• $H_0 = 0$ 是因为:如果我们代入 $n=0$ 进 $[x^n]\frac{g(x)}{(n-1)!} = [x^n]F(x)G(x)$,会发现右边为 $0$,所以 $H_0 = 0$。
则有:
$$[x^n]H(x) = [x^n]F(x)G(x)$$
注意到左右两个多项式的 $n$ 次项系数对于任何 $n$ 都相等,所以有
$$H(x) = F(x)G(x)$$
$$F(x) = \frac{H(x)}{G(x)}$$
由于 $H(x), G(x)$ 均已知,多项式求逆即可求出 $F(x)$,在每一项系数上乘以 $(n-1)!$ 即可得到 $f(x)$。
代码
#include <bits/stdc++.h>
using namespace std;
const int mod = 1004535809;
const int maxn = (1<<22) + 5;
// 多项式板子部分省略
ll f[maxn], g[maxn], G[maxn], invG[maxn], fac[maxn], invfac[maxn], h[maxn];
ll qpow(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int main() {
int n; cin >> n;
g[0] = 1;
for (ll i = 1; i <= n; i++) {
g[i] = qpow(2, i * (i-1) / 2);
}
fac[0] = 1;
for (int i = 1; i <= n; i++) fac[i] = fac[i-1] * (ll)i % mod;
invfac[n] = qpow(fac[n], mod-2);
for (int i = n-1; i >= 0; i--) invfac[i] = invfac[i+1] * (ll)(i+1) % mod;
h[0] = 0;
for (int i = 0; i <= n; i++) G[i] = g[i] * invfac[i] % mod;
for (int i = 1; i <= n; i++) h[i] = g[i] * invfac[i-1] % mod;
poly_inverse(G, invG, n+1);
poly_multiply(h, n+1, invG, n+1, f);
f[n] = f[n] * fac[n-1] % mod;
cout << f[n] << endl;
}
注意点
- 计数问题,想想
dp
思路? - 项数不能随便提出去,尤其是与 $n$ 有关的。遇到项数就转化成一个新的多项式来处理。
- 如果对于任何 $n$,左右两边的多项式第 $n$ 项系数相同,则这两个多项式相等。
例3 CF438E The Child and Binary Tree
题意
给定一个包含 distinct 正整数的集合 $\{c_1, c_2, …, c_n\}$。
给定整数 $m$,对于所有的 $s \in [1,m]$,求出满足以下条件的本质不同的 带点权二叉树个数:
- 每个顶点的权值必须在 $\{c_1, c_2, …, c_n\}$ 中。
- 所有顶点的权值之和为 $s$。
答案对 $998244353$ 取模。
其中,$1 \leq n,m,c_i \leq 10^5$。
题解
首先用 dp
的思路:
设 $f_n$ 为权值和为 $n$ 的二叉树个数(根的值 尚未确定),设 $g_n$ 为权值和为 $n$ 的二叉树个数(根的值 已经确定)。
则我们可以得出:
$$f_n = \sum\limits_{i=1}^n g_{n-c_i}$$
$$g_n = \sum\limits_{i=0}^n f_if_{n-i}$$
解释:
对于第一个式子,我们枚举根的值即可。
对于第二个式子,由于根的值已经确定,所以只需要枚举左右子树的权值和即可。
我们看到 $g_n = \sum\limits_{i=0}^n f_if_{n-i}$,发现这就是个卷积,所以有
$$g(x) = f(x)^2$$
但第一个式子怎么办呢?这是个加法,无法表示为卷积?
注意到本题中所有的权值 $\leq 10^5$,我们不如定义 $h_i$ 为:
$$h_i = \begin{cases}
1 ~~~~ \text{if } i \in \{c_1, c_2, …, c_n\} \\
0 ~~~~ \text{if } i \notin \{c_1, c_2, …, c_n\}
\end{cases}$$
则我们可以将 $f_n = \sum\limits_{i=1}^n g_{n-c_i}$ 转化为:
$$f_n = \sum\limits_{i=1}^nh_ig_{n-i}$$
这就又是个卷积了,代入 $n=0$ 可以发现:因为 $f_0 = g_0 = 1, h_0 = 0$,所以
$$f(x) = h(x)g(x) + 1 = h(x)f(x)^2 + 1$$
所以:
$$h(x)f(x)^2 - f(x) + 1 = 0$$
利用公式 $x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$ 有:
$$f(x) = \frac{1 \pm \sqrt{1-4h(x)}}{2h(x)}$$
有个问题,$h(x)$ 的常数项 $h(0) = 0$ 没法求逆?(注意到求逆的本质是 $h(x) * h^{-1}(x) = 1$)
没关系,没法求逆就乘到左边去,得到:
$$2f(x)h(x) = 1 \pm \sqrt{1-4h(x)}$$
代入 $x = 0$,由 $h(0) = 0$ 可以得到右边只能取 负号。
所以再把 $h(x)$ 除回去,得到:
$$f(x) = \frac{1 - \sqrt{1-4h(x)}}{2h(x)}$$
上下同乘 $(1 + \sqrt{1-4h(x)})$ 得到:
$$f(x) = \frac{2}{1 + \sqrt{1-4h(x)}}$$
多项式求逆求出 $f(x)$ 即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
const int maxn = (1<<21) + 5;
int n,m;
ll h[maxn], H[maxn];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int c; cin >> c;
h[c] = 1;
}
for (int i = 1; i <= 1e5; i++) {
h[i] = 4LL * h[i] % mod;
h[i] = (-h[i] + mod) % mod;
}
h[0] = (h[0] + 1) % mod;
poly_sqrt(h, H, 1e5+1);
H[0] = (H[0] + 1) % mod;
memset(h, 0, sizeof(h));
poly_inverse(H, h, 1e5+1);
for (int i = 1; i <= m; i++) {
cout << h[i] * 2LL % mod << "\n";
}
}
例4 洛谷P4451 整数的lqp拆分
题意
给定正整数 $N$,对于所有可能的 $m > 0$,一个整数拆分是一个有序数组 $(a_1,a_2,…,a_m)$ 满足:
- $a_i > 0$
- $a_1 + a_2 + … + a_m = N$
我们定义斐波那契数列为:
$$F_0 = 0, F_1 = 1, F_2 = 1, F_n = F_{n-1} + F_{n-2}$$
对于 $N$ 而言,它的一个整数拆分 $(a_1,a_2,…,a_m)$ 的权值为:
$$F_{a_1} * F_{a_2} * … * F_{a_m}$$
求所有可能的整数拆分的权值之和?形式化的,求:
$$\sum\limits_{(a_1,a_2,…,a_m)}\prod_{i=1}^mF_{a_i}$$
答案对 $10^9 + 7$ 取模。
其中,$1 \leq N \leq 10^{10000}$
题解
我们发现这个 $\sum$ 和 $\prod$ 的顺序其实可以调换的。
所以我们枚举一下第一个元素 $a_1$,设 $f_n$ 为我们所求的值,问题就转化为:
$$f_n = \sum\limits_{j=1}^nF_j*f_{n-j}$$
其中,$f_0 = 1$。
所以设
$$G(x) = F(x)f(x)$$
$$G(0) = F(0)f(0) = 0$$
所以有
$$f(x) = G(x) + 1 = F(x)f(x) + 1$$
得出
$$f(x) = \frac{1}{1-F(x)}$$
斐波那契数列的生成函数为(不记得了就手推一下):$$F(x) = \frac{x}{1-x-x^2}$$
所以得到:
$$f(x) = \frac{1-x-x^2}{1-2x-x^2} = 1-\frac{x}{x^2+2x-1}$$
我们发现这个常数项 $1$ 对我们的 $n$ 次项系数 $f_n$ 并没有任何影响,所以可以直接忽略。
现在我们需要裂项了,转化为表里面的形式:
$$f(x) = \frac{-x}{x^2+2x-1} = \frac{c}{1-ax} + \frac{d}{1-bx}$$
解方程就可以得到
$$b = -1\pm \sqrt 2, a = -1 \mp \sqrt 2$$
发现随便取哪个符号都一样(毕竟 $a,b$ 本就是对称的),所以有:
$$b = 1 + \sqrt 2, a = 1 - \sqrt 2, c = -\frac{1}{2\sqrt 2}, d = \frac{1}{2\sqrt 2}$$
所以
$$f(x) = \frac{c}{1-ax} + \frac{d}{1-bx}$$
$f(x)$ 的第 $n$ 次项系数为:
$$[x^n]f(x) = -\frac{1}{2 \sqrt 2}(a^n - b^n) = \frac{1}{2\sqrt 2}[(1+\sqrt 2)^n - (1-\sqrt 2)^n]$$
最后的问题就是如何求 $(1+\sqrt 2)^n - (1-\sqrt 2)^n$ ?
我们需要先知道 $\sqrt 2$ 在 $\text{mod }10^9 + 7$ 意义下的值,可以利用二次剩余解决。
然后对于 $n$,我们发现它出现在指数位置,而我们由费马小定理知道 $\forall a, a^{P-1} \equiv 1 (\text{mod } P)$
所以,我们只要将 $n$ 取一个 $n = n(\text{mod } (P-1))$ 即可。
代码
#include <bits/stdc++.h>
using namespace std;
const ll mod = 1e9+7;
const int maxn = 1e5+7;
// 求 sqrt(a) 在 mod P 下的值
// 调用 solve(a, P, r1, r2)
// 若有解,r1, r2 分别为两个解,其中 r1 小,r2 大
// 若无解,r1 == -1
namespace Quadratic_residue {
ll qpow(ll a, ll b, ll P) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % P;
a = a * a % P;
b >>= 1;
}
return res;
}
bool check_if_residue(ll x, ll P) {
return qpow(x, (P - 1) >> 1, P) == 1;
}
void solve(ll a, ll P, ll& r1, ll& r2) {
if (a <= 1) {
r1 = a, r2 = P - a; return;
}
if (!check_if_residue(a, P)) {
r1 = -1; return;
}
ll x;
while (1) {
x=1ll*rand()*rand()%P;
if (qpow((x*x-a+P)%P,(P-1)/2, P)!=1) break;
}
ll w=(x*x-a+P)%P;
pll res = {1,0}, t = {x,1};
auto Mul=[&](pll a,pll b){ // 复数乘法
ll x=(1ll*a.first*b.first+1ll*a.second*b.second%P*w)%P;
ll y=(1ll*a.first*b.second+1ll*a.second*b.first)%P;
return make_pair(x,y);
};
ll d=(P+1)/2;
while (d) {
if(d&1) res=Mul(res,t);
t=Mul(t,t);
d>>=1;
}
ll r = (res.first % P + P) % P;
r1 = min(r, (P - r) % P);
r2 = max(r, (P - r) % P);
}
};
ll s2, _; //sqrt(2)
ll qpow(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll inv(ll a) {
return qpow(a, mod-2);
}
ll readBigint() {
string s; cin >> s;
ll res = 0;
for (int i = 0; i < s.size(); i++) {
res = res * 10 % (mod - 1);
res = (res + (s[i] - '0')) % (mod - 1);
}
return res;
}
int main() {
ll n = readBigint();
Quadratic_residue::solve(2, mod, s2, _);
ll ans = qpow((1LL + s2) % mod, n) - qpow((1LL - s2 + mod) % mod, n);
ans = (ans + mod) % mod;
ans = ans * inv(2LL * s2 % mod) % mod;
cout << ans << "\n";
}
注意事项
$$a^b \equiv a^{b \text{ mod } (P-1)} (\text{mod } P)$$