QOJ7199 Bomb
我们考虑对于前 个点观察其导出子图的形态。当前会形成若干强连通分量,假设总共有 个。
就一定需要满足其中第 个强连通分量能够到达第 个强连通分量,否则不管后面如何加点都于事无补。因此为了保证强连通分量的极大性,第 个强连通分量不能够到达地 个强连通分量。
考虑当前加入一个点 ,对强连通分量产生的变化。
首先,需要满足限制:第 个强连通分量要能到达点 ,不然必然不合法。
此外,加入点 可以看成是先将点 作为第 个连通块。然后我们找到当前 向左能够到达最远的强连通分量,假设是第 个,那么就可以将的 个强连通分量到第 个连通块合并。
由于我们需要满足上面提到的限制,所以一个暴力的想法是,我们记录每个强连通分量向右到达最远位置。此时,一个性质是,对于新连通块向右到达最远位置,一定是第 个强连通分量向右到达最远位置和点 向右到达最远位置的较大值。
这是因为,如果存在第 个强连通分量向右到达最远位置比 远,那么说明存在一个点的半径比 大,由于它的坐标更左,所以理应在此之前就将第 个强连通分量合并了。
我们关注第一个连通块的信息,假设当前第一个连通块为前 个结点,且向右到达最远位置为 (并且对于 ,有 ),令前 个点半径平方和的最小值为 。
假设下一次第一个连通块拓展位置为 ,那么我们先声称 这部分的点,半径一定为 。
现在,我们来说明一下这部分的最优性。首先,由于前面提到的性质,我们并不关注 这部分点的向右到达最远位置,当然也不关注它们向左到达的最远位置,仅仅只要求它们能作为连通的一部分就好了。
假设存在点 ,满足点 到不了点 ,那么此时一定有 ,,存在 向 的连边,那么此时其贡献为 ,多出了 的贡献,由于 ,所以必然不优。
或者换一个角度理解,令点 向右到达最远位置为 ,那么为了保证连通性,一定需要有 能将 覆盖满。
而我们将第 个点的半径为 不仅保证了总和是下界,同时还保证了已经分配得尽可能均匀,所以是最优的。
于是我们考虑转移,枚举点 的向右到达最远位置为 。
那么可以得到:
此时,我们就会意识到 的作用是为了限制时刻都满足过程中 ,直到 。
现在,我们考虑优化转移,首先这个 可以用前缀和轻松处理。我们对两个 的取值分类讨论。
-
的取值为 :
这个时候,我们发现我们不关心 的取值,于是可以看成直接从 转移得到。
-
的取值为
得到 。我们可以枚举 ,再枚举 ,双指针可行找到 的最大可行值为 ,预处理 即可。
-
的取值为
那么我们可以枚举 ,再枚举 ,双指针找到 的最大值,最后求完之后对 做一遍后缀 chkmin 即可;
-
-
的取值为 :
朴素的想法是,我们可以使用斜率优化。
我们分析一下性质,发现由于过程中保持 不变,所以此时每次一定是从 转移到 。
时间复杂度 。
暴力代码:
void zhk() {
read(n);
F(i, 1, n) read(a[i]);
F(i, 2, n) s[i] = s[i - 1] + (ll) (a[i] - a[i - 1]) * (a[i] - a[i - 1]);
F(i, 1, n)
F(j, i, n)
dp[i][j] = INF;
F(i, 2, n) dp[1][i] = (ll) (a[i] - a[1]) * (a[i] - a[1]);
F(i, 1, n - 1) {
F(j, i + 1, n) {
F(k, i + 1, n) {
F(l, k, n)
chkmin(dp[k][max(j, l)], dp[i][j] + s[k] - s[i + 1] + (ll) max(a[l] - a[k], a[k] - a[i]) * max(a[l] - a[k], a[k] - a[i]));
}
}
}
cout << dp[n][n] << '\n';
}
转移优化代码:
void zhk() {
read(n);
F(i, 1, n) read(a[i]);
F(i, 2, n) s[i] = s[i - 1] + (ll) (a[i] - a[i - 1]) * (a[i] - a[i - 1]);
F(i, 1, n)
F(j, i, n)
dp[i][j] = INF;
F(i, 2, n) dp[1][i] = (ll) (a[i] - a[1]) * (a[i] - a[1]);
F(i, 2, n) {
g[i] = INF;
DF(j, i - 1, 1) g[j] = min(g[j + 1], dp[j][j + 1] - s[j + 1]);
int pos = 1;
DF(j, n, i) {
while (a[i] - a[pos] > a[j] - a[i]) pos++;
chkmin(dp[i][j], s[i] + g[pos] + (ll) (a[j] - a[i]) * (a[j] - a[i]));
chkmin(dp[i][j], dp[i - 1][j] + (ll) (a[i] - a[i - 1]) * (a[i] - a[i - 1]));
}
pos = n;
F(j, 1, i - 1) {
while (a[pos] - a[i] > a[i] - a[j]) pos--;
chkmin(dp[i][pos], s[i] - s[j + 1] + dp[j][j + 1] + (ll) (a[i] - a[j]) * (a[i] - a[j]));
}
DF(j, n - 1, i) chkmin(dp[i][j], dp[i][j + 1]);
}
cout << dp[n][n] << '\n';
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通