NC15052 求最值

题目

题目描述

给你一个长为 n 的序列 a

定义 f(i,j)=(ij)2+g(i,j)2

g 是这样的一个函数

img

求最小的 f(i,j) 的值,i!=j

输入描述

第一行一个数 n
之后一行 n 个数表示序列 a

输出描述

输出一行一个数表示答案

示例1

输入

4
1 0 0 -1

输出

1

备注

对于 100% 的数据,2n100000,|ai|10000

题解

方法一

知识点:分治,排序。

观察到, g(i,j)=(ij)2+(sum(j)sum(i))2 ,其中 sum(i)[1,i] 的前缀和。

将其转换为平面上的点 (i,sum(i)),(j,sum(j)) 则函数 g 为两点之间距离的平方,问题转换为平面最近点对问题。

这里一开始处理完已经是按 x 排序好的,直接开始分治。设区间左右端点为 l,r ,若 l==r ,则直接返回无穷大;若 l+1==r ,则返回两点距离;若其他情况则先求得对半左右两个区间的临时最小值 ans ,用这个 ans 作为阈值找到 x 轴坐标与区间分界坐标 mid 差的平方小于 ans 的点对,然后用朴素算法找到他们之间的最小值,过程中不断更新 ans 。在最后一步,有个关键优化,先按 y 轴排序,这样对于一个定点找另一点时,若 y 轴差值平方大于等于目前 ans 可以直接跳过,可以证明大部分情况可以将 O(n2) 降到 O(n)

时间复杂度 平均: O(nlog2n) 最差:O(n2logn)

空间复杂度 O(n)

方法二

知识点:分治,排序。

方法二实际上是方法一的优化。通过 [l,mid][mid+1,r]d2 最小值,返回合并区间的 [l,r]d2 最小值,在合并时用归并把当前区间的区间按 y 排序,方便查找合并区间的最小值,是 O(nlogn) 。如果每次不排序,直接取点 sorty 排序,会重复排序,最差会 O(n2)

要注意的是,如果采用这种方法我们就必须是先排序好再确定可行下标,但是确定下标需要原来 mid 处的 x 坐标,因此需要额外设置变量保存。

时间复杂度 O(nlog2n)

空间复杂度 O(n)

代码

方法一

#include <bits/stdc++.h>
#define ll long long
using namespace std;
struct Point {
ll x, y;
}a[100007];
int c[100007];
ll sqr(ll x) { return x * x; }
ll dist2(Point a, Point b) { return sqr(a.x - b.x) + sqr(a.y - b.y); }
ll solve(int l, int r) {
if (l == r) return ~(1LL << 63);
if (l + 1 == r) return dist2(a[l], a[r]);
int mid = l + r >> 1;
ll ans = min(solve(l, mid), solve(mid + 1, r));
int cnt = 0;
for (int i = l;i <= r;i++) if (sqr(a[i].x - a[mid].x) < ans) c[cnt++] = i;
sort(c, c + cnt, [&](int p, int q) {return a[p].y < a[q].y;});
for (int i = 0;i < cnt;i++) {
for (int j = i + 1;j < cnt;j++) {
if (sqr(a[c[i]].y - a[c[j]].y) >= ans) break;
ans = min(ans, dist2(a[i], a[j]));
}
}
return ans;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) a[i].x = i, cin >> a[i].y, a[i].y += a[i - 1].y;
cout << solve(1, n) << '\n';
return 0;
}

方法二

#include <bits/stdc++.h>
#define ll long long
using namespace std;
struct Point {
ll x, y;
}a[100007], b[100007];
ll c[100007];
ll sqr(ll x) { return x * x; }
ll dist2(Point a, Point b) { return sqr(a.x - b.x) + sqr(a.y - b.y); }
ll solve(int l, int r) {
if (l == r) return ~(1LL << 63);
if (l + 1 == r) return dist2(a[l], a[r]);
int mid = l + r >> 1;
ll midx = a[mid].x;
ll ans = min(solve(l, mid), solve(mid + 1, r));
merge(a + l, a + mid + 1, a + mid + 1, a + r + 1, b + l, [&](Point a, Point b) {return a.y < b.y;});
for (int i = l;i <= r;i++) a[i] = b[i];
int cnt = 0;
for (int i = l;i <= r;i++) if (sqr(a[i].x - midx) < ans) c[cnt++] = i;
for (int i = 0;i < cnt;i++) {
for (int j = i + 1;j < cnt;j++) {
if (sqr(a[i].y - a[j].y) >= ans) break;
ans = min(ans, dist2(a[i], a[j]));
}
}
return ans;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) a[i].x = i, cin >> a[i].y, a[i].y += a[i - 1].y;
cout << solve(1, n) << '\n';
return 0;
}
posted @   空白菌  阅读(74)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示