NC15052 求最值

题目

题目描述

给你一个长为 \(n\) 的序列 \(a\)

定义 \(f(i,j)=(i-j)^2+g(i,j)^2\)

\(g\) 是这样的一个函数

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

输入描述

第一行一个数 \(n\)

之后一行 \(n\) 个数表示序列 \(a\)

输出描述

输出一行一个数表示答案

示例1

输入

4
1 0 0 -1

输出

1

备注

对于 \(100\%\) 的数据,\(2 \leq n \leq 100000 , |ai| \leq 10000\)

题解

思路

方法一

知识点:分治,排序。

观察到, \(g(i,j) = (i-j)^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(n^2)\) 降到 \(O(n)\) 。

时间复杂度 平均: \(O(n\log^2n)\) 最差:\(O(n^2 \log n)\)

空间复杂度 \(O(n)\)

方法二

知识点:分治,排序。

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

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

时间复杂度 \(O(n\log^2n)\)

空间复杂度 \(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;
}

NC15052 求最值的更多相关文章

  1. ACM3 求最值

    /*2*2014.11.18*求最值*描述:给定N个整数(1<=N<=100),求出这N个数中的最大值,最小值.*输入:多组数据,第一行为一个整数N,第二行为N个不超过100的正整数,用空 ...

  2. [NOI2005]维修数列 Splay tree 区间反转,修改,求和,求最值

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1500 Description Input 输入文件的第1行包含两个数N和M,N表示初始时数 ...

  3. hdu4521-小明系列问题——小明序列(线段树区间求最值)

    题意:求最长上升序列的长度(LIS),但是要求相邻的两个数距离至少为d,数据范围较大,普通dp肯定TLE.线段树搞之就可以了,或者优化后的nlogn的dp. 代码为  线段树解法. #include ...

  4. javascript之求最值

    求最值: var selections = $("#deliveryGridSalesOrGoods").datagrid('getRows'); var costPrice = ...

  5. poj3264(线段树区间求最值)

    题目连接:http://poj.org/problem?id=3264 题意:给定Q(1<=Q<=200000)个数A1,A2,```,AQ,多次求任一区间Ai-Aj中最大数和最小数的差. ...

  6. Sql示例说明如何分组后求中间值--【叶子】

    原文:Sql示例说明如何分组后求中间值--[叶子] 这里所谓的分组后求中间值是个什么概念呢? 我举个例子来说明一下: 假设我们现在有下面这样一个表: type        name price -- ...

  7. hdu 1754 I Hate It(树状数组区间求最值)2007省赛集训队练习赛(6)_linle专场

    题意: 输入一行数字,查询第i个数到第j个数之间的最大值.可以修改其中的某个数的值. 输入: 包含多组输入数据. 每组输入首行两个整数n,m.表示共有n个数,m次操作. 接下来一行包含n个整数. 接下 ...

  8. C语言 · 求arccos值

    算法提高 7-2求arccos值   时间限制:10.0s   内存限制:256.0MB      问题描述 利用标准库中的cos(x)和fabs(x)函数实现arccos(x)函数,x取值范围是[- ...

  9. 位运算求最值 学习笔记 (待补充QAQ)

    没有什么前言?直接进入正题qwq 俩俩异或 求最值: 建trie树 O(n)枚举每个数找这个数的最值,每次反走就成,还可以剪枝一波(如果在某位已经小于ans显然可以直接return? void Ins ...

随机推荐

  1. Day 005:PAT练习--1047. 编程团体赛(20)

    编程团体赛的规则为:每个参赛队由若干队员组成:所有队员独立比赛:参赛队的成绩为所有队员的成绩和:成绩最高的队获胜.现给定所有队员的比赛成绩,请你编写程序找出冠军队. 输入格式: 输入第一行给出一个正整 ...

  2. 聊聊Lock接口的lock()和lockInterruptible()有什么区别?

    lock()和lockInterruptible()都表示获取锁,唯一区别是,当A线程调用lock()或lockInterruptible()方法获取锁没有成功而进入等待锁的状态时,若接着调用该A线程 ...

  3. XCTF练习题---MISC---can_has_stdio?

    XCTF练习题---MISC---can_has_stdio? flag:flag{esolangs_for_fun_and_profit} 解题步骤: 1.观察题目,下载附件 2.打开发现是由tra ...

  4. 『现学现忘』Git对象 — 17、Commit对象

    目录 1.Commit对象介绍 2.Commit对象说明 3.本地库中对象之间的关系 4.总结 5.练习 6.本文用到的命令总结 1.Commit对象介绍 现在来介绍最后一种Git对象commit对象 ...

  5. gcc版本切换 Linux环境下

    先看看我们系统用的gcc和g++是什么版本:gcc -v 可能会出现安装失败(E: 软件包gcc还没有可供安装的候选者): 1.使用如下命令修改源 sudo gedit /etc/apt/source ...

  6. 详解六大国产CPU处理器

    一个执着于技术的公众号 CPU作为计算机设备的运算和控制核心,负责指令读取.译码与执行,因研发门槛高.生态构建难,被认为是集成电路产业中的"珠穆朗玛峰". 纵观全球,Intel.A ...

  7. install dns server on ubuntu

    参考 CSDN/Ubuntu环境下安装和配置DNS服务器 在 Ubuntu 上安裝 DNS server Install BIND 9 on Ubuntu and Configure It for U ...

  8. 虚拟机:ESX

    VMware ESXi 与ESX 产品之比较   VMware vSphere 5.0 以后版本,所有底层虚拟化产品都改为ESXi产品,本文主要比较了ESXi与ESX的各自特点,以便对大家是否要把现有 ...

  9. 好客租房3-React的基本使用

    2.1React的安装 安装命令:npm i react react-dom react 包是核心,提供创建元素,组件等功能 react-dom包提供DOM相关功能等 2.2React的使用 1引入r ...

  10. Git标签用法

    我们通常会在项目开发到一定阶段时给代码打上标签. 1.Git查看所有标签及其描述信息 git tag -l -n 2.Git创建标签 创建标签并添加描述信息 git tag -a v1.0.0 -m ...