题解:P17603 「RiOI-8」宝生 マーゴ

· · 题解

思路

$$ f_i = \max(f_{i-m}+\frac{c!}{m^m},f_{i-1}) $$ 可以对 $f$ 乘上 $m^m$ 最后再除掉。 由于要比较大小,所以要存储精确值,把每个数表示成阶乘: $$ x=\sum_{j\ge1}d_j\,j!,\qquad 0\le d_j\le j $$ 对于这种进制,比较大小可以直接从高往低比较,加法就按位模拟。 容易发现 $f_i$ 只有最多 $O(m+\log n)$ 位,若直接存储转移,复杂度是 $O(nm)$ 的。还有一种存储方式是存所有非零位的值,非零位最多只有 $\frac{n}{m}$ 个,时间复杂度为 $O(\frac{n^2}{m})$。 根号分治一下,时间复杂度为 $O(n \sqrt{n})$。但是空间爆了。注意到 $f$ 只要保留最新的 $m$ 个即可,空间复杂度 $O(n)$。可以通过。 ## 代码 ::::info[code] ```cpp #include<bits/stdc++.h> using namespace std; const int mod = 998244353; int n, m, w; bool fl; vector<vector<int> > f; vector<int> g, len; inline int qpow(int a, int b) { int res = 1; while (b) { if (b & 1) res = 1ll * res * a % mod; a = 1ll * a * a % mod, b >>= 1; } return res; } inline void add1(int p) { g[p] ++; while (g[p] > p) { g[p] = 0; p ++; g[p] ++; } } inline void add2(int p, int &s) { int l = 1; while (l <= s && g[l] < p) l ++; int r = l; while (r + p - 1 <= s && g[r + p - 1] == p) { r += p; p ++; } if (l == r) for (int i = s; i >= l; i --) g[i + 1] = g[i]; else for (int i = r; i <= s; i ++) g[i + 1 - r + l] = g[i]; g[l] = p; s = s - (r - l) + 1; } inline bool chk(int s, int v) { int t = len[v]; while (s && t) { if (g[s] != f[v][t]) return g[s] > f[v][t]; s --, t --; } return s > t; } signed main() { ios :: sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n >> m; int k = n / m, h = m + 1; if (k == 1) { vector<int> a(n + 1), cnt(m + 1); int z = 0, bad = 0, mx = -1; for (int i = 1; i <= n; i ++) { cin >> a[i]; if (!a[i]) z ++; else if (++ cnt[a[i]] == 2) bad ++; if (i > m) { int x = a[i - m]; if (!x) z --; else if (cnt[x] -- == 2) bad --; } if (i >= m && !bad) mx = max(mx, z); } int ans = (mx >= 0); for (int i = 2; i <= mx; i ++) ans = 1ll * ans * i % mod; cout << 1ll * ans * qpow(qpow(m, m), mod - 2) % mod << '\n'; return 0; } for (int t = k; t > 1; t >>= 1) h ++; fl = m <= k; w = fl ? h : k; f.assign(m, vector<int> (w + 1)); g.resize(w + 1); len.assign(m, fl ? w : 0); vector<int> a(n + 1), cnt(m + 1), fac(h + 1); fac[0] = 1; for (int i = 1; i <= h; i ++) fac[i] = 1ll * fac[i - 1] * i % mod; int z = 0, bad = 0; for (int i = 1; i <= n; i ++) { cin >> a[i]; if (a[i] == 0) z ++; else if (++ cnt[a[i]] == 2) bad ++; if (i > m) { int x = a[i - m]; if (x == 0) z --; else if (cnt[x] -- == 2) bad --; } if (i < m) continue; int u = i % m, v = (i - 1) % m; int s = len[u]; bool t = 0; if (bad == 0) { for (int j = 1; j <= s; j ++) g[j] = f[u][j]; int p = max(z, 1); if (fl) add1(p); else add2(p, s); t = chk(s, v); } if (t) { len[u] = s; for (int j = 1; j <= s; j ++) f[u][j] = g[j]; } else { len[u] = len[v]; for (int j = 1; j <= len[v]; j ++) f[u][j] = f[v][j]; } } int u = n % m; long long ans = 0; for (int j = 1; j <= len[u]; j ++) { if (fl) ans += 1ll * f[u][j] * fac[j]; else ans += fac[f[u][j]]; } cout << ans % mod * qpow(qpow(m, m), mod - 2) % mod; return 0; } ::::