[ROI 2018] Innophone 题解
[ROI 2018] Innophone
看了半天网上仅有的一篇题解……才堪堪写出来
不过在LOJ上看提交,全是
KTT
,看得我瑟瑟发抖(不会
题意翻译
在平面上有一些点,你需要在这个平面上任意确定一个点(不要求是给定的点),定义其贡献为 横坐标 \(\times\) 其右侧的点 \(+\) 纵坐标 \(\times\) 其左上方的点:
红色部分是右侧的点,蓝色部分是左上方的点。
具体边界参考题目!
我们需要求最大的贡献值。
解题
其实不难发现,目标点的横坐标一定是图中某一个点的横坐标,否则我们可以在固定纵坐标的情况下将点向右移,使得贡献变大。
所以问题简化为如何求蓝色部分的最大贡献?
我们先考虑横坐标固定的情况。
设 \(f(i)\) 表示左侧部分纵坐标大于 \(i\) 的点的个数。那么我们需要求的是
\]
我原本猜测这可能会是一个单峰函数,但是模拟了一组数据:
发现似乎并不单峰……似乎每一次只能暴力求解?
所以我们继续考虑每次加入一个点对于这个函数的影响。
不难发现,其实每一次是把一部分的后缀 \(i * f(i)\) 变为 \((i + 1) * f(i)\)。
而对于后缀的 \(+1\),或许可以想到分块打标记(逃。
而考虑每一个块打了标记之后,我们需要求的其实是 \((i + tag) * f(i)\) 的值。
考虑设 \(b = (i + tag) * f(i)\),可以转化为 \(i * f(i) = -tag * f(i) + b\)。
在这个式子中最大化 \(b\),这是我们思考到斜率优化问题。
具体来说,也就是在每一块中维护一个上凸包,又由于 \(tag\) 是递增的……依据斜率维护一个双端队列即可。
然而加入一个点的过程呢?我们直接把它所在的块暴力重构即可。
于是我们可以得到更新后的左侧答案 cans
,更新总答案即可。
复杂度分析
这里我尝试使用核算法 (
accounting method
) 进行分析其实代码中有我用英文写的分析……主要是没有配置输入法……
考虑每一次加入一个点,会重构一个块,花费了 \(\sqrt n\),而重构凸包,花费是 \(2 * \sqrt n\),其中有一半是提前为凸包中的元素支付的。这使得在一个块中标记的花费为 \(0\)。也就是说总的复杂度为 \(O(n \sqrt n)\)。
这么看着复杂度确实不够优秀,所以这需要代码常数优秀才可。
我犯病有
deque
做双端队列……导致开了O2
才能过。不过考虑到用
deque
对于边界条件的明示作用更大,所以我还是贴上这个写法。
代码
#include <iostream>
#include <algorithm>
#include <deque>
#include <cmath>
using namespace std;
const int N = 2e5;
typedef long long lint;
// BLO stands for block length, n is the points' count
int BLO, n;
struct P {
int x, y;
bool operator < (const P &p) {
return x < p.x;
}
} p[N];
int apps[N << 2], apc = 0;
void discrete() {
static auto gt = greater<int>();
for (int i = 1; i <= n; ++i) {
apps[i] = p[i].y;
}
sort(apps + 1, apps + 1 + n, gt);
int *ae = unique(apps + 1, apps + 1 + n);
for (int i = 1; i <= n; ++i) {
p[i].y = lower_bound(apps + 1, ae, p[i].y, gt) - apps;
}
apc = ae - apps - 1;
BLO = sqrt(apc);
}
void read() {
cin >> n;
for (int x, y, i = 1; i <= n; ++i) {
cin >> x >> y;
p[i] = {x, y};
}
sort(p + 1, p + 1 + n);
}
// save the sum of one point
int s[N];
class BLOCK {
private:
int l, r, tag;
deque<int> Q; // for using slope optimize
public:
void set(int l, int r) {
this->l = l, this->r = r;
}
// rebuild this block.
// with many points that can build a convex tull
// which can help use maintain the max value.
inline lint build() {
lint ans = 0;
// we calc every point of the BLOCK, to get the answer for whole block after rebuilding.
for (int i = l; i <= r; ++i) {
ans = max(ans, 1ll * (s[i] += tag) * apps[i]);
} tag = 0;
Q.clear();
int siz = 0;
for (int i = r; i >= l; --i) {
#define Y(i) (1ll * apps[i] * (s[i] + tag))
#define X(i) (1ll * apps[i])
// build reversely, for using the convex hull to right.
// now add point (X(i), Y(i)), We should make sure slope(i) >= slope(Q[last])
// Consider Q[siz -1] as j, Q[siz - 2] as k
// So we need
// ( Y(i) - Y(j) ) / ( X(i) - X(j) ) >= ( Y(j) - Y(k) ) / ( X(j) - X(k) )
// as the followed line
while (siz > 1 && ( Y(i) - Y(Q[siz -1]) ) * ( X(Q[siz - 1]) - X(Q[siz - 2]) ) >= ( Y(Q[siz - 1]) - Y(Q[siz - 2]) ) * ( X(i) - X(Q[siz - 1]) ))
Q.pop_back(), --siz;
Q.push_back(i), ++siz;
}
return ans;
}
lint add() {
++tag;
while (Q.size() > 1 && Y(Q[0]) <= Y(Q[1]))
Q.pop_front();
return Y(Q[0]);
}
} blocks[700];
lint cans = 0;
// now we consider why the complexity is correct?
// we using accounting method to analyze it.
// First, every time we rebuild a block, we cost sqrt(n) to build
// and we build a deque, which at most have sqrt(n) items
// This cost 2*sqrt(n) (half prepayed for adding tag).
// so when we are adding tag, we cost nothing.
// Second, when querying, we use O(1) however.
// In total, we have n times add, which makes complexity to O(n \sqrt n)
inline void add(int y) {
int b = y / BLO;
int r = min((b + 1) * BLO, apc + 1); // without r
// update one block's s and need rebuild!!
for (int i = y; i < r; ++i) {
++s[i];
}
cans = max(cans, blocks[b].build());
for (int i = b + 1, ie = apc / BLO; i <= ie; ++i) {
cans = max(cans, blocks[i].add());
}
}
int main() {
cin.tie(0)->sync_with_stdio(false);
read();
discrete();
for (int i = 0, l = 0, r = BLO - 1; l <= apc; ++i, l += BLO, r += BLO) {
blocks[i].set(max(l, 1), min(r, apc));
blocks[i].build();
}
lint res = 0;
for (int i = 1; i <= n + 1; ++i) {
res = max(res, 1ll * (n - i + 1) * p[i].x + cans); // update ans for (n + 1) times !!!
if (i <= n) add(p[i].y);
}
cout << res << '\n';
return 0;
}
后记
正如我在开头所说,可以使用 KTT
解决这道题。而且复杂度非常优秀,似乎是 \(O(n \log^2 n)\)?不是特别了解。只是知道它跑得飞快。
思路却也与此相似。可以发现,其实每一次操作我们是在 \(i * f(i)\) 的基础上加上一个 \(f(i)\) 函数。或者这正也是为什么 KTT
可以做这道题的原因。单纯口胡……
[ROI 2018] Innophone 题解的更多相关文章
- Codeforces:Good Bye 2018(题解)
Good Bye 2018! 题目链接:https://codeforces.com/contest/1091 A. New Year and the Christmas Ornament 题意: 给 ...
- JOI 2018 Final 题解
题目列表:https://loj.ac/problems/search?keyword=JOI+2018+Final T1 寒冬暖炉 贪心 暴力考虑每相邻两个人之间的间隔,从小到大选取即可 #incl ...
- JXOI 2018 简要题解
目录 「JXOI2018」游戏 题意 题解 代码 「JXOI2018」守卫 题意 题解 代码 「JXOI2018」排序问题 题意 题解 代码 总结 「JXOI2018」游戏 题意 可怜公司有 \(n\ ...
- Codeforces Round #505 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final) 题解
真心简单的一场比赛 就是坑比较多(自己太蠢) A是一个水题 3分钟的时候过了 B也是一个比较简单的题 类似的套路见得多了 但是我当时可能比较困 想了一会才想出来 19分钟的时候过掉了 C同样很显然 性 ...
- 2019 湖南多校第一场(2018~2019NCPC) 题解
解题过程 开场shl过B,C,然后lfw写J,J WA了以后shl写A,但是因为OJ上空间开小WA了,而不是MLE?,J加了特判过了.之后一直在检查A错哪了,直到qt发现问题改了空间,浪费许多时间,但 ...
- NOIP 2018 简要题解
从这里开始 Day 1 Problem A 考虑贪心地选取极大非 0 段减少. 如果两次操作有交,并且不是包含关系,那么把其中一次操作的,但另一次没有操作的移过去,然后就变成了上面那个贪心了. Cod ...
- codechef February Challenge 2018 简要题解
比赛链接:https://www.codechef.com/FEB18,题面和提交记录是公开的,这里就不再贴了 Chef And His Characters 模拟题 Chef And The Pat ...
- HNOI 2018 简要题解
寻宝游戏 毒瘤题. 估计考试只会前30pts30pts30pts暴力然后果断走人. 正解是考虑到一个数&1\&1&1和∣0|0∣0都没有变化,&0\&0& ...
- NOIP 2018 day1 题解
今年noip的题和去年绝对是比较坑的题了,但是打好的话就算是普通水准也能350分以上吧. t1: 很显然这是一个简单的dp即可. #include<iostream> #include&l ...
- BZOJ5249:[九省联考2018]IIIDX——题解
https://www.luogu.org/problemnew/show/P4364#sub https://www.lydsy.com/JudgeOnline/problem.php?id=524 ...
随机推荐
- Web3开发者技术选型:前端视角(next.js)
引言 在现代Web开发的世界中,Web3技术的兴起为前端开发者开辟了新的可能性.Web3技术主要指的是建立在区块链基础上的分布式网络,使用户能够通过智能合约和去中心化应用(DApps)直接交互,而无需 ...
- 实时 OLAP, 从 0 到 1
简介: BTC.com 团队在实时 OLAP 方面的技术演进过程及生产优化实践. 作者|高正炎 本文主要介绍 BTC.com 团队在实时 OLAP 方面的技术演进过程及生产优化实践,内容如下: 业务背 ...
- [FAQ] 阿里云一口价域名购买之后在哪里看
进入控制台,产品和服务中找到"域名",进去后在左侧菜单有 "已买到的域名". 如图: Link:https://www.cnblogs.com/farwish/ ...
- 2018-2-13-win10-uwp-自定义控件-SplitViewItem
title author date CreateTime categories win10 uwp 自定义控件 SplitViewItem lindexi 2018-2-13 17:23:3 +080 ...
- 在网页上直接运行Win11,5秒内用AI克隆自己的声音 | 蛮三刀酱的Github周刊第二期
大家好,这里是每周更新的Github精彩分享周刊,我是每周都在搬砖的蛮三刀酱. 我会从Github热门趋势榜里选出 高质量.有趣,牛B 的开源项目进行分享. 1. PowerShell:不止于Wind ...
- rails 给数据库表里加入索引
创建迁移文件 rails g migration add_product_images_index 编写迁移文件 db/migrate/20170119093958_add_product_image ...
- python教程8-页面爬虫
python爬虫常用requests和beautifulSoup这2个第三方模块.需要先进行手动安装. requests负责下载页面数据,beautifulSoup负责解析页面标签. 关于beauti ...
- postgresql 去重&查最新一组记录 关键词partition by
- OpenVoiceV2本地部署教程,苹果MacOs部署流程,声音响度统一,文字转语音,TTS
最近OpenVoice项目更新了V2版本,新的模型对于中文推理更加友好,音色也得到了一定的提升,本次分享一下如何在苹果的MacOs系统中本地部署OpenVoice的V2版本. 首先下载OpenVoic ...
- 使用自定义lua解析管理器调用lua脚本中的table
[5] 使用自定义lua解析管理器调用table 访问数组类型的table CallLuaEntrance测试脚本中内容: //------------------------------------ ...