题面蛮长的,大概是定义了两个函数
嗯,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;
}
京公网安备 11010502036488号