CF1285F Classical?

题目链接

首先看到 \(lcm\) 就可能想到枚举 \(\gcd\),只考虑 \(\gcd\) 倍数的数。现在我们的任务是,从一堆数中找到 \(\gcd\) 恰好\(g\)\(x,y\),最大化 \(x \times y\)。所有合法数都除以 \(g\) 以后变成从一堆数中找到互质的 \(x,y\),最大化 \(x \times y\)

这样有什么优势呢?显然,\(x \times y\)\(lcm(x,y)\) 有更多的好性质可以利用。我们有一个基于 \(x \times y\) 的贪心做法:从大到小枚举 \(x\),并在之前找到与 \(x\) 互质的最大的 \(y\),用 \(x \times y\) 更新答案,然后小于等于 \(y\) 的数就可以扔掉了(以后的 \(x\) 会越来越小,不扔也更新不了答案)。这样我们就有了一种类似单调栈的做法,就可以 \(O(nlnn)\) 通过此题了

等等,如何找到与 \(x\) 互质的最大的 \(y\)?我们或许需要知道栈里面有多少个数与 \(x\) 互质,设其为 \(g(x)\)。然后发现它有点像数论推式子:(设 \(A\)\(max(a_i)\),数 \(i\) 的出现次数为 \(c_i\)

\[\begin{aligned} g(x) &= \sum_{i = 1}^A [(i,x)=1]c_i \\ &= \sum_{i=1}^Ac_i\sum_{d|i,d|x}\mu(d) \\ &= \sum_{d|x} \mu(d)\sum_{i=1}^A[d|i]c_i \\ &= \sum_{d|x} \mu(d)F(d) \end{aligned}\]

其中 \(F(x)\) 可以在每个元素入栈时算。这样,就可以在 \(O(logn)\) 左右的时间内算出栈内与当前 \(x\) 互质的数的个数。

总复杂度:\(O(nlog^2n)\)

关键代码:

int mucnt[N];
inline int Count(int x) {
	int res = 0;
	for (register uint j = 0; j < vec[x].size(); ++j) {
		int to = vec[x][j];
		res += mu[to] * mucnt[to];
	}
	return res;
}
inline void calc(int bs) {
	stop = 0;
	for (register int i = htot; i; --i) {
		int ct = Count(h[i]);
		while (ct) {
			int tmp = stk[stop--];
			int g = get_gcd(tmp, h[i]);
			if (g == 1) {
				MAX(ans, 1ll * tmp * h[i] * bs);
				--ct;
			}
			for (register uint j = 0; j < vec[tmp].size(); ++j)
				--mucnt[vec[tmp][j]];
		}
		stk[++stop] = h[i];
		for (register uint j = 0; j < vec[h[i]].size(); ++j) {
			int to = vec[h[i]][j];
			++mucnt[to];
		}
	}
	for (register int i = htot; i; --i)
		for (register uint j = 0; j < vec[h[i]].size(); ++j)
			mucnt[vec[h[i]][j]] = 0;
}

int main() {
	for (register int i = 1; i <= up; ++i)
		for (register int j = i; j <= up; j += i)
			vec[j].push_back(i);
	for (register int i = 1; i <= up; ++i) {
		htot = 0;
		for (register int j = i, k = 1; j <= up; j += i, ++k)	if (bin[j])	h[++htot] = k;
		calc(i);
	}
	printf("%lld\n", ans);
	return 0;
}
posted @ 2020-08-31 19:50  JiaZP  阅读(106)  评论(0编辑  收藏  举报