20250808 做题记录
早上在看卡牌游戏,差不多理解了做法。
下午打多校,做了一些签到,不能算什么题吧,顶多把那个蓝给完成了。
晚上十点通过了 卡牌游戏,并写了如下题解:
发现 \(\operatorname{lcm}(a,b)\) 可以扩展为任意 \(a,b\) 的公倍数,因为显然用 \(\operatorname{lcm}\) 不劣。
设 \(dp[i]\) 表示以 \(i\) 结尾,最大的结果。
看到有除法,考虑根号分治。把 \(\le B=\sqrt S\) 的称为小数,\(>B\) 的称为大数。
如果 \(a[i]\) 是小数,则我们开一个桶,\(ans[i]\) 表示当前数为 \(i\) 时的 \(dp\) 值。直接 dp[i] = ans[a[i]]; 即可。同时要更新一下后文的 \(mx\)。
如果 \(a[i]\) 是大数,
- 首先考虑 \(a[j]\) 是小数。发现 \(\operatorname{lcm}\) 无法对应到 \(a[j]\)(因为 \(a[j]\) 的倍数太多了)。但是 \(a[j]\) 本身的取值比较少,因此我们也开一个桶,\(mx[i]\) 表示 \(a[j]=i\) 时最大的 \(dp\) 值。我们枚举上一个数的值,更新
dp[i] = mx[j] + S / t * t;。 - 如果 \(a[j]\) 是大数,我们就可以枚举 \(\operatorname{lcm}\) 了,我们也可以从 \(ans\) 转移。不过在 \(i>B\) 的时候 \(ans[i]\) 的定义不太一样了:定义为 \(a[i]a[j]|i\) 时,最大的 \(dp\) 值。因此枚举 \(a[i]\) 的倍数,
dp[i] = ans[j * a[i]];,同时如果 \(a[i]a[j]>S\),都计入 \(ans[S+1]\),即dp[i] = ans[S + 1];。
然后更新 \(ans\):ans[j * a[i]] = dp[i] + S / (a[i] * j) * (a[i] * j);,以及ans[S + 1] = dp[i];。
最后为了让 \(ans\le B\) 的部分正确,我们可以用当前的 \(dp\) 值更新 \(ans\):ans[j] = dp[i] + S / lcm(a[i], j) *lcm(a[i], j);。
这样我们就做完了第一部分。
对于 \(tp=1\) 的情况,考虑拼接:对于转移,要么被 ban 的点是被跨过的,要么就是刚好踩在一个转移点上,因此我们只需要再往前多记录一个转移点就行了。
发现可能过不了,我没写。
考虑常数优化:
注意到一个性质:如果 \(dp[i]=dp[j]+c(j,i)\),\(c(j,i)\) 要么为 \(0\),要么 \(>\frac S2\)。因此如果 \(S\ge\max(a[i])^2\),则能选的都可以选上,因为不会有 \(c(j,i)=0\) 的情况。更进一步,如果一个转移 \(j\to i\),\(i,j\) 之间有 \(\ge3\) 个小数,则我们不妨把这三个小数都选上,一定更优。因此,对于一个 \(i\),如果从一个小数转移到它,只需要枚举往前走 \(1\sim3\) 步的小数,如果它转移到一个小数,只需枚举往后走 \(1\sim3\) 步的小数。
那么,从大数转移到大数怎么办呢?考虑仍然用之前的转移即可。记 \(mul[i]\) 表示 \(\max_{a[j]|i}dp[j]\),转移就枚举 \(a[i]\) 的倍数 \(j\),\(dp[i]\gets mul[j]+j\)。(因为 \(j\) 就是最开始的“公倍数”)然后再更新 \(mul\)。这里要注意,倍数可以为 \(0\),不要忽略了。
那么,怎么做 \(tp=1\) 呢?就跟前面说的,你本来只需要记录前 3 个转移点,现在为了防止 ban 的刚好踩在一个上面,你就多记录一个就行了,然后在转移的时候把两个转移点中间的答案 check max。
如果是大数转移到大数,注意到 dp 值单调,只需要记录最后一个是 \(i\) 的因数的位置和倒数第二个,就可以转移了。
你会有 \(n\sqrt S\) 个 check max 操作,使用反向 st 表搞一搞就行了。

浙公网安备 33010602011771号