【洛谷 8 月月赛 II】P6876 「SWTR-6」GCDs & LCMs
原题链接:https://www.luogu.com.cn/problem/P6786
首先抛出两个显而易见的式子,对于任意正整数 $i,j,k$ 均有:
- $\gcd(k \times i, \ k \times j) = k \times \gcd(i,j)$
- $\text{lcm}(k \times i, \ k \times j) = k \times \text{lcm}(i,j)$
很容易解释这两个式子的正确性,因为左式相当于两个数因子都多了一个 $k$,所以最大公约数、最小公倍数的值也肯定是原来的 $k$ 倍。
接下来就可以开始处理题面中的式子了(这里为了方便,将式子进行了移项):
定义 $f(i,j)=i+j+\gcd(i,j)-\text{lcm}(i,j)$。
也就是说若 $f(i,j)=0$ 则 $i+j+\gcd(i,j)=\text{lcm}(i,j)$。
对于任意三个正整数 $k,x,y$,根据上面的结论就有
$$
\begin{aligned}
f(k\times x,k\times y)
&= k\times x + k\times y + \gcd(k\times x,k\times y)-\text{lcm}(k\times x,k\times y) \\
&= k\times x+k\times y+k\times\gcd(x,y)-k\times\text{lcm}(x,y)\\
&= k \times f(x,y)\\
\end{aligned}$$
也就是对于任意正整数 $i,j,k$,如果满足 $f(i,j)=0$,那么也有 $f(k \times i,k \times j)=0$。
那代表着我们只需要讨论哪些互质的正整数对 $(i,j)$ 满足式子值为 $0$。
这样一来,条件就简化成:$i+j+1-i\times j=0$,因式分解一下就是 $(i-1)(j-1)=2$。
令 $i\le j$,因为 $i,j$ 都是正整数,所以满足条件的只有一组解:
$$\left\{
\begin{aligned}
i & = 2\\
j & = 3\\
\end{aligned}
\right.
$$
也就是 $f(2,3)=0$。
而根据之前的结论,对于正整数 $k$ 均有 $f(2k,3k)=0$。
换句话说,对于正整数 $i,j\ (i \le j)$ 有 $i+j+\gcd(i,j)=\text{lcm}(i,j)$,当且仅当 $\frac{i}{j}=\frac{2}{3}$。
做到这里,题目就简化了许多:
从 $a$ 序列中找出若干个数,使得这些数中每个数要么是这些数中最大的,要么存在另一个数是自己的 $\frac{3}{2}$ 倍,最后找到这些数和的最大值。
之后流程就很简单了,这里简单梳理一下:
- 将序列 $a$ 从小到大排序。
- 令 $dp_i$ 表示所有最大值为 $a_i$ 的选出序列中,这些数和(不包含自己)的最大值。
- 如果序列中第 $i$ 个数与它前面的数相同,那么 $dp_i=dp_{i-1}+a_{i-1}$。
- 否则,如果 $a_i$ 是 $3$ 的倍数,那么 $dp_i=dp_{a_{i\times 2/3}的位置}+a_{a_{i\times 2/3}的位置}$ 。
- 找到最大的 $dp_i+a_i$。
具体如何找到有序序列中某个数在序列中最后的位置,使用 STL 或 hash 都可以解决(以下代码使用 std::map)。
代码
#include <algorithm> #include <cstdio> #include <map> #define N 300010 typedef long long ll; using namespace std; int n, a[N]; bool b[N]; ll dp[N], ans; map <int, int> m; inline ll max(ll a, ll b){ return a > b ? a : b; } int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); std::sort(a + 1, a + n + 1); // 排序 for(int i = 1; i <= n; i++) m[a[i]] = i; for(int i = 1; i <= n; i++){ if(a[i] == a[i - 1]) dp[i] = max(dp[i], dp[i - 1] + a[i]); else if(a[i] % 3 == 0) dp[i] = dp[m[a[i] / 3 * 2]] + a[m[a[i] / 3 * 2]]; } for(int i = 1; i <= n; i++) ans = max(ans, dp[i] + a[i]); // 因为 a[i] 没有算进 dp[i],所以最后需要加进去 printf("%lld\n", ans); return 0; }