AtCoder Regular Contest 135 F Delete 1, 4, 7, ...
神仙题……
记 \(f(n)\) 为一次操作后第 \(n\) 个数是多少,则 \(f(n) = \left\lfloor\frac{3n + 1}{2}\right\rfloor\)。
记 \(f^k(n) = f(f^{k-1}(n))\),\(f^0(n) = n\),\(h_k\) 为 \(k\) 次操作后剩下的数的个数,\(h_k = \left\lfloor\frac{2h_{k-1}}{3}\right\rfloor\),\(h_0 = n\),那么:
暴力计算是 \(O(h_kk) = O(nk (\frac{2}{3})^k)\) 的。
\(k\) 较大时能使用上面的做法,下面考虑 \(k\) 较小的情况。
不知道为什么能看出来有一个结论 \(f^k(n + 2^k) = f^k(n) + 3^k\)。考虑归纳证明。
- \(k = 1\) 时由于题目性质显然成立。
- \(k > 1\) 时:
到这一步,枚举 \(i \in [0, 2^k - 1]\),计算 \(f^k(i)\),那么所有 \(n \equiv i \pmod {2^k}\) 的 \(f^k(n)\) 都能求出。时间复杂度为 \(O(k2^k)\),但是还不够。
考虑折半。把 \(f^k(x)\) 拆分成 \(f^y(f^x(n))\),其中 \(x + y = n\)。枚举 \(i \in [0, 2^x - 1]\),即求:
设 \(F(a, b) = \sum\limits_{p=0}^a f^y(b + 3^x p)\),即求 \(\sum\limits_{i=0}^{2^x-1} F(\left\lfloor\frac{h_k - i}{2^x}\right\rfloor, f^x(i))\)。
发现 \(F(a, b)\) 仍然不好求,不妨二进制拆分,设 \(g(a, i) = \sum\limits_{p=0}^{2^i-1} f^y(a + 3^x p)\),那么 \([0, a]\) 能拆分成 \(\log a\) 个区间 \([c, c + 2^j - 1]\)。一个区间的答案为:
那么现在问题转化成了求 \(g(a, i)\)。
边界是 \(g(a, 0) = f^y(a)\)。
这样求 \(g(a, b)\) 可采用记忆化搜索实现,但是复杂度寄了,因为 \(a\) 可能很大。
考虑缩小 \(a\) 的规模。发现当 \(a \ge 2^y\) 时,令 \(b = \left\lfloor\frac{a}{2^y}\right\rfloor\),\(c = a \bmod 2^y\),则:
至此,这个做法的复杂度为 \(O((y + \log n) \max(2^x, 2^y))\)。取 \(x = \left\lfloor\frac{k}{2}\right\rfloor, y = \left\lceil\frac{k}{2}\right\rceil\),复杂度 \(O((k + \log n) 2^{\left\lceil\frac{k}{2}\right\rceil})\)。
于是我们现在得到了一个 \(O(nk (\frac{2}{3})^k)\) 和一个 \(O((k + \log n) 2^{\left\lceil\frac{k}{2}\right\rceil})\) 的做法。把两种做法拼起来,设一个阈值 \(B\),\(k > B\) 执行第一种做法,\(k \le B\) 执行第二种做法。取 \(B = 40\) 较优。
终于做完了,好累啊。
code
// Problem: F - Delete 1, 4, 7, ...
// Contest: AtCoder - AtCoder Regular Contest 135
// URL: https://atcoder.jp/contests/arc135/tasks/arc135_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const ll mod = 998244353;
ll n, m, x, y, h[110], d[1050000][55], pw[110];
inline ll f(ll k, ll n) {
while (k--) {
n = (n * 3 + 1) / 2;
}
return n;
}
ll g(ll a, ll i) {
if (a >= (1LL << y)) {
ll b = (a >> y), c = (a & ((1LL << y) - 1));
return (g(c, i) + (1LL << i) % mod * (b % mod) % mod * (pw[y] % mod) % mod) % mod;
}
if (d[a][i] != -1) {
return d[a][i];
}
if (!i) {
return d[a][i] = f(y, a) % mod;
}
return d[a][i] = (g(a, i - 1) + g(a + (pw[x] << (i - 1)), i - 1)) % mod;
}
void solve() {
mems(d, -1);
scanf("%lld%lld", &n, &m);
h[0] = n;
for (int i = 1; i <= m; ++i) {
h[i] = h[i - 1] * 2 / 3;
}
pw[0] = 1;
for (int i = 1; i <= 32; ++i) {
pw[i] = pw[i - 1] * 3;
}
if (m > 40) {
ll ans = 0;
for (int i = 1; i <= h[m]; ++i) {
ans = (ans + f(m, i)) % mod;
}
printf("%lld\n", ans);
return;
}
x = m / 2;
y = m - x;
ll ans = 0;
for (ll i = 0; i < (1LL << x); ++i) {
ll up = ((h[m] - i) >> x) + 1, a = f(x, i), c = 0;
for (int j = 50; ~j; --j) {
if (up >= (1LL << j)) {
up -= (1LL << j);
ans = (ans + g(a + c * pw[x], j)) % mod;
c += (1LL << j);
}
}
}
printf("%lld\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号