题解:【HT-093-Rainbow】G - 高中生数学题
这里为了避免使用 \(x\),将题面中的 \(x\) 写成 \(t\)。
这个形式很像多项式乘法的系数,用生成函数表示答案:
\[[x^n]\left(\sum_{j=0}^{+\infty}\sin(jt)x^j\right)^m
\]
(由于 \(\sin0=0\) 所以可以将求和下界改为 \(0\)。)
一个很典的转换就是注意到:
\[e^{ix}=\cos x+i\sin x
\]
\[\sin x=\Im e^{ix}
\]
那么答案就可以改成:
\[\begin{aligned}
[x^n]\left(\sum_{j=0}^{\infty}\Im e^{ijt}x^j\right)^m&=[x^n]\left(\Im\sum_{j=0}^{\infty}(e^{it}x)^j\right)^m \\
&=[x^n]\left(\Im\frac{1}{1-e^{it}x}\right)^m
\end{aligned}
\]
欸,这个求 \(\Im\) 好像不能移到次方外面。那这样就只能暴力快速幂 + 多项式求逆计算了,可以过 \(n\) 比较小的点。
我们再想一想怎么算,回顾一下这个式子。
\[e^{ix}=\cos x+i\sin x
\]
其实我们可以直接利用 \(\sin x=\frac{e^{ix}-e^{-ix}}{2i}\),然后再写出生成函数。
\[\begin{aligned}
\prod_{j=1}^m\sin(k_jt)&=\prod_{j=1}^m\frac{e^{ik_jt}-e^{-ik_jt}}{2i}
\end{aligned}
\]
这个时候再将答案写成生成函数的形式,应该就长这样:
\[\begin{aligned}
[x^n]\left(\sum_{j=0}^{+\infty}\frac{e^{ijt}-e^{-ijt}}{2i}x^j\right)^m&=[x^n]\left(\frac 1{2i(1-e^{it}x)}-\frac 1{2i(1-e^{-it}x)}\right)^m \\
&=[x^n]\left(\frac 1{2i}\frac{e^{it}x-e^{-it}x}{(1-e^{-it}x)(1-e^{it}x)}\right)^m \\
&=\left(\frac{e^{it}-e^{-it}}{2i}\right)^m[x^n]\left(\frac{x}{1-(e^{it}-e^{-it})x+x^2}\right)^m \\
&=\sin^m(t)[x^{n-m}]\frac 1{(1-2\cos(t)x+x^2)^m}
\end{aligned}
\]
多项式快速幂 + Bostan-Mori 求解分式远程系数即可。
#include <bits/stdc++.h>
using namespace std;
const int kM = 1e9 + 7;
int T, n, m, x, y;
int Fpow(int a, int b, int p, int r = 1) {
for (; b; a = 1LL * a * a % p, b >>= 1) {
if (b & 1) r = 1LL * r * a % p;
}
return r % p;
}
vector<int> operator*(vector<int> &a, vector<int> &b) {
vector<int> c(a.size() + b.size() - 1);
for (int i = 0; i < a.size(); i++) {
for (int j = 0; j < b.size(); j++) {
c[i + j] = (c[i + j] + 1LL * a[i] * b[j]) % kM;
}
}
return c;
}
vector<int> Fpow(vector<int> a, int b, vector<int> r = {1}) {
for (; b; a = a * a, b >>= 1) {
if (b & 1) r = r * a;
}
return r;
}
int BostanMori(int n, vector<int> f, vector<int> g) {
for (int i; n; n >>= 1) {
vector<int> h = g;
for (int i = 0; i < h.size(); i++) {
h[i] = i % 2 ? kM - g[i] : g[i];
}
f = f * h, g = g * h;
for (i = n % 2; i < f.size(); i += 2) f[i / 2] = f[i];
f.resize(i / 2);
for (i = 0; i < g.size(); i += 2) g[i / 2] = g[i];
g.resize(i / 2);
}
if (!f.size()) return 0;
return 1LL * f[0] * Fpow(g[0], kM - 2, kM) % kM;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
for (cin >> T; T; T--) {
cin >> n >> m >> y >> x;
cout << BostanMori(n - m, {Fpow(y, m, kM)}, Fpow({1, (kM - 2 * x % kM) % kM, 1}, m)) << '\n';
}
return 0;
}

浙公网安备 33010602011771号