CF1285F Classical?

题目链接

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

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

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

[egin{aligned} g(x) &= sum_{i = 1}^A [(i,x)=1]c_i \ &= sum_{i=1}^Ac_isum_{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
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/JiaZP/p/13591469.html