题面蛮长的,大概是定义了两个函数
嗯,t(1e5)和m(1-5)还有那些个素数是给定的,现在给你个1e9的N,让你算
。
那么第一很明显的是,欧拉函数是积性函数,而的公式是个迪利克雷卷积形式,所以显然
也是积性函数,所以我们就可以得到
然后这个形式他就很想让人用生成函数。根据欧拉函数的性质.
因此
其实到这里都没什么难度,我们只要求出的系数就好了。题解说是线性递推,但是很多人(好吧只有我)不知道为什么能转化成线性递推。
然后那天在群里问,有人说展开,我会想起了斐波那契的生成函数解法,于是才想到。
事实上是这样的将生成函数上线展开,设成这样。
然后移过来
(其实到这里参考生成函数求斐波那契,大家应该会了)也就是
我们假设
考虑等式(1)两边的每项系数
...
...
到这里你应该看出来了,的前
项系数可以用多项式求逆(对
)和
的卷积得到,而后面可以用线性递推得到。
后面就不写了,大家可以参考很多地方的齐次线性递推的blog or 板子,就很简单。
最后放一发队友的代码(多项式求逆,取模,递推,快速幂板子我全是抄的逆十字的所以就不放了)
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; typedef pair<ll, ll> pll; typedef vector<int> vi; #define endl '\n' // const int mod = 1e9 + 7; const int mod = 998244353, G = 3, Gi = 332748118; const int inf = 1e9; const double pi = acos(-1); #define int ll typedef vector<int> poly; int lim; int rev[2100000], p[100005], ip[500005]; int n, t, m; void invSieve(int n, int p) { ip[0] = ip[1] = 1; for (int i = 2; i <= n; i++) { ip[i] = (ll) (p - p / i) * ip[p % i] % p; } } ll ksm(ll a, ll x) { ll ans = 1; while (x) { if (x & 1) ans = ans * a % mod; a = a * a % mod; x /= 2; } return ans; } ll inv(int x) { return ksm(x, mod - 2); } void init(int n) { for(lim = 1; lim < n; lim <<= 1); for(int i = 0; i < lim; ++i) rev[i] = rev[i >> 1] >> 1 | ((i & 1) ? lim >> 1 : 0); } int sub(int a, int b) {return a < b ? a - b + mod : a - b; } // TODO: NTT void NTT(int n, poly &a, int t) { for (int i = 0; i < n; ++i) { if (i < rev[i]) swap(a[i], a[rev[i]]); } for (int len = 1; len <= (n >> 1); len <<= 1) { int wn = ksm(t == 1 ? G : Gi, (mod - 1) / (len * 2)); for (int i = 0; i <= n - (len << 1); i += (len << 1)) { int w = 1; for (int j = 0; j < len; ++j) { int x = a[i + j], y = 1ll * w * a[i + j + len] % mod; a[i + j] = (x + y) % mod; a[i + j + len] = sub(x, y); w = 1ll * w * wn % mod; } } } if (t == -1) { int ki = inv(n); for(int i = 0; i < n; ++i) a[i] = 1ll * a[i] * ki % mod; } } poly operator * (poly a, int b) { for (int i = 0; i < a.size(); ++i) a[i] = 1ll * a[i] * b % mod; return a; } poly operator + (poly a, poly b) { poly c = a; c.resize(max(a.size(), b.size())); for (int i = 0; i < b.size(); ++i) c[i] = (c[i] + b[i]) % mod; return c; } poly operator - (poly a, poly b) { poly c = a; c.resize(max(a.size(), b.size())); for (int i = 0; i < b.size(); ++i) c[i] = sub(c[i], b[i]); return c; } // TODO: 多项式乘法 poly operator * (poly a, poly b) { int deg = a.size() + b.size() - 1; if (a.size() <= 32 || b.size() <= 32) { poly c(deg, 0); for (int i = 0; i < a.size(); ++i) for (int j = 0; j < b.size(); ++j) (c[i + j] += 1ll * a[i] * b[j] % mod) %= mod; return c; } init(deg); a.resize(lim), b.resize(lim); NTT(lim, a, 1), NTT(lim, b, 1); for (int i = 0; i < lim; ++i) a[i] = 1ll * a[i] * b[i] % mod; NTT(lim, a, -1); a.resize(deg); return a; } // TODO: 多项式微分 poly Dif(poly a) { for (int i = 0; i + 1 < a.size(); i++) a[i] = 1ll * a[i + 1] * (i + 1) % mod; a.pop_back(); return a; } // TODO: 多项式积分 poly Int(poly a) { for (int i = a.size() - 1; i > 0; i--) a[i] = 1ll * a[i - 1] * inv(i) % mod; a[0] = 0; return a; } // TODO: 多项式求逆 poly Inv(poly a, int deg = -1) { if (deg == -1) deg = a.size(); poly b(1, inv(a[0])), c; for (int len = 2; (len >> 1) < deg; len <<= 1) { c.resize(len); init(len << 1); for (int i = 0; i < len; i++) c[i] = i < a.size() ? a[i] : 0; c.resize(lim), b.resize(lim); NTT(lim, c, 1), NTT(lim, b, 1); for (int i = 0; i < lim; i++) b[i] = b[i] * (998244355 - 1ll * c[i] * b[i] % mod) % mod; NTT(lim, b, -1); b.resize(len); } b.resize(deg); return b; } // TODO: 多项式ln poly Ln(poly a, int deg = -1) { if (deg == -1) deg = a.size(); a = Dif(a) * Inv(a, deg); a.resize(deg); a = Int(a); return a; } // TODO: 多项式exp poly Exp(poly a, int deg = -1) { if (deg == -1) deg = a.size(); poly b(1, 1), c; int n = a.size(); for (int len = 2; (len >> 1) < deg; len <<= 1) { c = Ln(b, len); for (int i = 0; i < len; i++) { c[i] = sub((i < n ? a[i] : 0), c[i]); } c[0]++; b = b * c; b.resize(len); } b.resize(deg); return b; } // TODO: 多项式除法 poly operator / (poly a, poly b) { int deg = a.size() - b.size() + 1; reverse(a.begin(), a.end()); a.resize(deg); reverse(b.begin(), b.end()); a = a * Inv(b, deg); a.resize(deg); reverse(a.begin(), a.end()); return a; } // TODO: 多项式取模 poly operator % (poly a, poly b) { if (a.size() < b.size()) return a; a = a - (b * (a / b)); a.resize(b.size() - 1); return a; } poly solve(int l, int r) { if (l == r) { poly a = {1, mod - p[(l - 1) / m + 1]}; return a; } int mid = (l + r) / 2; return solve(l, mid) * solve(mid + 1, r); } signed main() { ios::sync_with_stdio(false); cin.tie(0); #ifndef ONLINE_JUDGE freopen("a.in", "r", stdin); freopen("a.out", "w", stdout); #endif cin >> n >> t >> m; int N = m * t; poly a; int C = 1, f = 1; for (int i = 1; i <= t; i++) cin >> p[i]; invSieve(N, mod); for (int i = 0; i <= N; i++, f *= -1) { a.push_back(f == 1 ? C : mod - C); C = 1ll * C * (N - i) % mod * ip[i + 1] % mod; } // for (int i : a) cout << i << ' '; // cout << endl; poly b = solve(1, N); // for (int i : b) cout << i << ' '; // cout << endl; poly c = a * Inv(b); // for (int i : c) cout << i << ' '; // cout << endl; c.resize(N + 1); if (n <= N) cout << (c[n] + mod) % mod << endl, exit(0); reverse(b.begin(), b.end()); n--; poly t0 = {1}, t1 = {0, 1}; while (n) { if (n & 1) t0 = t0 * t1 % b; t1 = t1 * t1 % b; n /= 2; } int ans = 0; t0.resize(N); for (int i = 0; i < N; i++) { ans = (ans + 1ll * c[i + 1] * t0[i]) % mod; } cout << (ans + mod) % mod << endl; }