QOJ7199 Bomb

我们考虑对于前 i 个点观察其导出子图的形态。当前会形成若干强连通分量,假设总共有 k 个。

就一定需要满足其中第 i 个强连通分量能够到达第 i+1 个强连通分量,否则不管后面如何加点都于事无补。因此为了保证强连通分量的极大性,第 i 个强连通分量不能够到达地 i1 个强连通分量。

考虑当前加入一个点 i,对强连通分量产生的变化。

首先,需要满足限制:第 k 个强连通分量要能到达点 i,不然必然不合法。

此外,加入点 i 可以看成是先将点 i 作为第 k+1 个连通块。然后我们找到当前 i 向左能够到达最远的强连通分量,假设是第 p 个,那么就可以将的 p 个强连通分量到第 k+1 个连通块合并。

由于我们需要满足上面提到的限制,所以一个暴力的想法是,我们记录每个强连通分量向右到达最远位置。此时,一个性质是,对于新连通块向右到达最远位置,一定是第 p 个强连通分量向右到达最远位置和点 i 向右到达最远位置的较大值。

这是因为,如果存在第 q(q>p) 个强连通分量向右到达最远位置比 i 远,那么说明存在一个点的半径比 i 大,由于它的坐标更左,所以理应在此之前就将第 p 个强连通分量合并了。

QQ20250219-102551


我们关注第一个连通块的信息,假设当前第一个连通块为前 i 个结点,且向右到达最远位置为 j(并且对于 in,有 j>i),令前 i 个点半径平方和的最小值为 dpi,j

假设下一次第一个连通块拓展位置为 k,那么我们先声称 p[i+1k1] 这部分的点,半径一定为 ap+1ap

QQ20250219-102610

现在,我们来说明一下这部分的最优性。首先,由于前面提到的性质,我们并不关注 i+1k1 这部分点的向右到达最远位置,当然也不关注它们向左到达的最远位置,仅仅只要求它们能作为连通的一部分就好了。

假设存在点 p,满足点 p 到不了点 p+1,那么此时一定有 l<prp+1,存在 lr 的连边,那么此时其贡献为 (a+b)2,多出了 b2+2abc2 的贡献,由于 bc,所以必然不优。

QQ20250219-103646

或者换一个角度理解,令点 p 向右到达最远位置为 nxtp,那么为了保证连通性,一定需要有 [p,nxtp] 能将 [i+1,k1] 覆盖满。

而我们将第 p 个点的半径为 ap+1ap 不仅保证了总和是下界,同时还保证了已经分配得尽可能均匀,所以是最优的。


于是我们考虑转移,枚举点 k 的向右到达最远位置为 l

那么可以得到:

dpi,j+p=i+1k1(ap+1ap)2+max{alak,akai}2dpk,max{j,l}

此时,我们就会意识到 j 的作用是为了限制时刻都满足过程中 j>i,直到 i=n

现在,我们考虑优化转移,首先这个 p=i+1k1(ap+1ap)2 可以用前缀和轻松处理。我们对两个 max 的取值分类讨论。

  1. max{j,l} 的取值为 l

    这个时候,我们发现我们不关心 j 的取值,于是可以看成直接从 dpi,i+1 转移得到。

    1. max{alak,akai} 的取值为 alak
      alakakai 得到 ai2akal

      我们可以枚举 k,再枚举 l,双指针可行找到 i 的最大可行值为 pos,预处理 gp=mint=pi1dpt,t+1 即可。

    2. max{alak,akai} 的取值为 akai

      那么我们可以枚举 k,再枚举 i,双指针找到 l 的最大值,最后求完之后对 dpi 做一遍后缀 chkmin 即可;

  2. max{j,l} 的取值为 j

    朴素的想法是,我们可以使用斜率优化。

    我们分析一下性质,发现由于过程中保持 j 不变,所以此时每次一定是从 i 转移到 i+1

时间复杂度 O(n2)

暴力代码:

Copy
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'; }

转移优化代码:

Copy
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'; }
posted @   zhaohaikun  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示