不算太难的题,duel 的时候用了 1h 切掉了。
注意到 (恭喜你你做完了这个题最难的一步)。最后的 是容易省去的,于是问题可以转化为:
对于给定的 值,你可以构造一棵 个点的完全图,其中两点 的边权是 ,记 为按照上述步骤构造的图的 MST 边权之和,问 是否是有上界的。
先考虑对于给定的 值怎么快速求出构造出完全图的 MST 大小。把所有点的点权 按照从小到大的顺序排序,那么显然此时有 。容易想到记 是满足 的 里最小的值,那么直接贪心的把 都和 连边, 都和 连边,容易证明该贪心策略一定可以得到最优解。
把这个式子写出来,即:
化简式子:
容易发现这个东西如果对 已知 那么可以预处理 序列前缀和然后做到 计算答案。
但是问题是 的取值范围是 ,显然无法枚举所有的实数 来计算答案。考虑缩小最优解的 的取值集合。这里注意到对 , 的取值都不会变化,而显然对同一个 上面的式子是单峰的答案一定取在两个端点上,因此需要判定的 的集合只有 ,而此时显然 的值也是十分容易求出的。因此该算法时间复杂度优化到 可以通过该题。
最后剩下答案为 INF 的情况,直接带入 和 即可简单判定(只需要看此时 的项的系数是不是 的即可)。
该算法对于单组数据时间复杂度为 ,可以轻松通过该题。
namespace Loyalty
{
int a[N], pre[N];
inline void init() {}
inline void main([[maybe_unused]] int _ca, [[maybe_unused]] int _atc)
{
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
sort(a + 1, a + n + 1);
int v1 = 0, v2 = 0;
for (int i = 2; i <= n; ++i)
v1 += a[1] + a[i];
for (int i = 1; i < n; ++i)
v2 += a[i] + a[n];
if (v1 > 0 || v2 < 0)
cout << "INF\n";
else
{
for (int i = 1; i <= n; ++i)
pre[i] = pre[i - 1] + a[i];
int res = -inf;
for (int i = 1; i <= n; ++i)
{
int v1 = a[1] * (pre[n] - pre[i]) + a[n] * (pre[i] - pre[1]);
int v2 = a[i] * (a[1] * (n - i) + a[n] * (i - 1) + pre[n] - pre[1]);
res = max(res, v1 - v2);
}
cout << res << '\n';
}
}
} // namespace Loyalty
喜欢的话,留下你的评论吧~