AtCoder Regular Contest 135 F Delete 1, 4, 7, ...

洛谷传送门

AtCoder 传送门

神仙题……

\(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\),那么:

\[ans = \sum\limits_{n=1}^{h_k} f^k(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\) 时:

\[f^k(n + 2^k) \]

\[= f^{k-1}(f(n + 2^k)) \]

\[= f^{k-1}(\left\lfloor\frac{3(n + 2^k) + 1}{2}\right\rfloor) \]

\[= f^{k-1}(\left\lfloor\frac{3n + 1}{2}\right\rfloor + 3 \times 2^{k-1}) \]

\[= f^{k-1}(\left\lfloor\frac{3n + 1}{2}\right\rfloor) + 3^k \]

\[= f^k(n) + 3^k \]

到这一步,枚举 \(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]\),即求:

\[\sum\limits_{p=0}^{\left\lfloor\frac{h_k - i}{2^x}\right\rfloor} f^k(i + 2^x p) \]

\[= \sum\limits_{p=0}^{\left\lfloor\frac{h_k - i}{2^x}\right\rfloor} f^y(f^x(i + 2^x p)) \]

\[= \sum\limits_{p=0}^{\left\lfloor\frac{h_k - i}{2^x}\right\rfloor} f^y(f^x(i) + 3^x p) \]

\(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]\)。一个区间的答案为:

\[\sum\limits_{p=c}^{c + 2^j - 1} f^y(a + 3^x p) \]

\[= \sum\limits_{p=0}^{2^j - 1} f^y(a + 3^x (c + p)) \]

\[= \sum\limits_{p=0}^{2^j - 1} f^y(a + c 3^x + p 3^x) \]

\[= g(a + c3^x, j) \]

那么现在问题转化成了求 \(g(a, i)\)

\[g(a, i) = \sum\limits_{p=0}^{2^i-1} f^y(a + 3^x p) \]

\[= \sum\limits_{p=0}^{2^{i-1}-1} f^y(a + 3^x p) + \sum\limits_{p=2^{i-1}}^{2^i-1} f^y(a + 3^x p) \]

\[= \sum\limits_{p=0}^{2^{i-1}-1} f^y(a + 3^x p) + \sum\limits_{p=0}^{2^{i-1}-1} f^y(a + 3^x (p + 2^{i-1})) \]

\[= \sum\limits_{p=0}^{2^{i-1}-1} f^y(a + 3^x p) + \sum\limits_{p=0}^{2^{i-1}-1} f^y(a + 2^{i-1} 3^x + 3^x p) \]

\[= g(a, i - 1) + g(a + 2^{i-1} 3^x, i - 1) \]

边界是 \(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\),则:

\[g(a, i) = \sum\limits_{p=0}^{2^i - 1} f^y(a + 3^x p) \]

\[= \sum\limits_{p=0}^{2^i - 1} f^y(c + 3^x p + 2^y b) \]

\[= \sum\limits_{p=0}^{2^i - 1} (f^y(c + 3^x p) + 3^y b) \]

\[= g(c, i) + 2^i 3^y b \]

至此,这个做法的复杂度为 \(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;
}

posted @ 2023-05-13 11:10  zltzlt  阅读(30)  评论(0)    收藏  举报