P5896 [IOI2016]aliens
DP 优化方法大杂烩,详解 wqs 二分及其注意事项,斜率优化等其它 DP 优化方法。
**** 团队赛 T6,没想到是 IOI 原题。当时看出来要用 wqs 二分,但是我没学过(经常发生知道要用某种算法但是不会写的情况),因此最近学习了一下 wqs 二分。找题目练手的时候看到了这题,世 界 线 收 束。做题的过程中发现还要用到斜率优化,于是也学了一下。
这算是一道比较套路的题目,但套路并不意味这不是好题,毕竟我从中学到了 wqs 二分和斜率优化两种 DP 优化方法。说它套路其实也不太合适,因为 2016 年 wqs 二分还没有在 OI 界普及,甚至国外将其称之为 “Aliens’ Trick” 就是根据本题的名字而命名的,所以这个 idea 在当时应该比较新颖。
不妨对网格图的主对角线所经过的格子从左上到右下依次编号为 \(0,1,2,\cdots,m-1\)。显然,编号为 \(i\) 的格子在原网格图中的坐标为 \((i,i)\)。因为正方形的主对角线完全包含于网格图的主对角线,所以为了拍到一个点 \((r_i,c_i)\) 必然要拍到编号为 \(l_i,l_i+1,l_i+2,\cdots,r_i\) 的格子,其中 \(l_i=\min(r_i,c_i),r_i=\max(r_i,c_i)\)。因此,我们可以将题目转化为线段覆盖问题:现在有 \(n\) 条线段 \([l_i,r_i]\),我们要用最多 \(k\) 个区间 \([x_i,y_i]\) 去覆盖它们,使得每条线段至少被一个区间完全包含。即 \(\forall i,\exist j,s.t. [l_i,r_i]\in [x_j,y_j]\)。求以每个区间为对角线形成的所有正方形的面积并的最小值。接下来我们可以挖掘一些性质:
\(\textbf {Observation 1.}\) 如果两条线段 \(i,j\) 满足 \([l_i,r_i]\in [l_j,r_j]\),那么线段 \(i\) 对最终的答案没有影响。
正确性显然。根据该性质,我们可以将所有线段按照左端点 \(l_i\) 从小到大为第一关键字,右端点 \(r_i\) 从大到小为第二关键字排序。然后依次遍历所有线段并记录右端点最大值 \(r\)。若当前线段 \(i\) 的右端点 \(r_i\leq r\),则忽略线段 \(i\);否则用 \(r_i\) 去更新 \(r\),即 \(r\gets r_i\)。最终所有没有被忽略的线段,一定满足左端点和右端点都单调递增。读者自证不难。
\(\mathbf{Observation\ 2.}\) 最终所选取的所有区间一定满足 \(x_i<x_{i+1},y_i<y_{i+1}\ (1\leq i<k)\)。
读者自证不难。
\(\mathbf{Observation\ 3.}\) 设 \(F_i\) 为恰好选取 \(i\) 个区间时答案的最小值,那么 \(F_i\) 是一个下凸函数。
读者自证不很难。感性理解一下即可,严格证明的话应该比较复杂。
有了上述性质,我们可以愉快地进行 DP 了:wqs 二分斜率 \(K\),然后设 \(f_i\) 表示覆盖第 \(i\) 条线段时的最小代价(线段按照左端点从小到大排序。根据性质 \(1\) 的结论,右端点也行,因为不影响线段之间的顺序)。显然有转移方程:
\]
这个分情况讨论很讨厌,尝试把它消灭掉:设 \(g_i=(y_i-x_{i+1}+1)^2[y_i\geq x_{i+1}\land i>0]\),那么转移方程变为 \(f_i=\min_{j=0}^{i-1}f_j+(y_i-x_{j+1}+1)^2-g_j-K\)。显然的斜率优化,但是我不会,所以多讲一点:拆开变成 \(f_i=\min_{j=1}^{i-1} f_j+(y_i+1)^2-2(y_i+1)x_{j+1}+x^2_{j+1}-g_j-K\)。先将所有 \(y_i\) 加上 \(1\)(不影响单调性),忽略掉 \(\min\) 再移移项,写成 \(Y=kX+b\) 的形式,\(f_j+x^2_{j+1}-g_j-K=2y_ix_{j+1}+(f_i-y_i^2)\)。那么 \(Y=f_j+x^2_{j+1}-g_j-K\),\(k=2y_i\),\(X=x_{j+1}\),\(b=f_i-y^2_i\)。将 \((X_j,Y_j)\) 看做一个点,因为 \(k\) 确定了,现在就要找使得 \(b\) 最小的 \((X_j,Y_j)\)(这样能使得 \(f_i\) 最小)。由于 \(k,X\) 都是单调递增的,所以单调队列维护下凸包即可。
具体地,记 \(k_{i,j}\) 为点 \((X_i,Y_i)\) 与 \((X_j,Y_j)\) 之间的斜率。首先将点 \(0\) 加入单调队列,接下来遍历从小到大每个位置 \(i\):
- 若单调队列大小不小于 \(2\),设 \(a,b\) 分别为队首的第一个和第二个点,若 \(k_{a,b}\leq k=2y_i\) 则 \(b\) 一定不劣于 \(a\),弹出队首。重复该操作直到单调队列大小为 \(1\) 或 \(k_{a,b}>2y_i\)。
- 此时队首 \(a\) 即为最优决策 \(j\),\(f_j\to f_i\) 转移并记录选择的区间个数。
- 若单调队列大小不小于 \(2\),设 \(c,d\) 分别为队尾的倒数第二个和倒数第一个点;若 \(k_{c,d}\geq k_{d,i}\) 则 \(d\) 一定不在下凸包上,弹出队尾。重复该操作直到单调队列大小为 \(1\) 或 \(k_{c,d}<k_{d,i}\)。
- 将点 \(i\) 加入单调队列。
我们需要求出在代价最小的前提下最少的区间个数。不妨设 \(num_i\) 为覆盖前 \(i\) 条线段且代价最小时最少的区间个数 ,那么因为 \(num_i\) 显然有单调性,所以我们每次转移时只需要选择最前面的一个决策点转移即可,即第一步操作弹出队首时,将条件 \(k_{a,b}\leq 2y_i\) 改为 \(k_{a,b}<2y_i\),这样就保证了相同优劣的决策点,前者不会被后者所弹出(第三步的条件不需要改,因为是前面的决策点弹出后面的决策点,不影响结果)。
时间复杂度 \(\mathcal{O}(n\log V)\),其中 \(V\) 是二分范围,本题中为 \(m^2\)。浮点数比较建议使用 \(eps\)。代码倒是不难写。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
const int N=1e5+5;
const ld eps=1e-10;
ll n,m,k,c,ans;
struct seg{
int l,r;
bool operator < (const seg &v) const{
return l!=v.l?l<v.l:r>v.r;
}
}p[N];
ll K,f[N],g[N],num[N];
ll sq(ll x){return x*x;}
ll Y(int i){return f[i]+sq(p[i+1].l)-g[i]-K;}
ll X(int i){return p[i+1].l;}
ld slope(int i,int j){return (ld)(Y(j)-Y(i))/(X(j)-X(i));}
ll hd,tl,d[N];
bool check(){
d[hd=tl=1]=0;
for(int i=1;i<=n;i++){
while(hd<tl&&slope(d[hd],d[hd+1])+eps<=2*p[i].r)hd++;
int j=d[hd]; num[i]=num[j]+1,f[i]=f[j]+sq(p[i].r-p[j+1].l)-g[j]-K;
if(i<n)while(hd<tl&&slope(d[tl-1],d[tl])-eps>=slope(d[tl],i))tl--;
d[++tl]=i;
} return num[n]<=k;
}
int main(){
cin>>n>>m>>k;
for(int i=1,c,r;i<=n;i++){
cin>>p[i].l>>p[i].r;
if(p[i].l>p[i].r)swap(p[i].l,p[i].r);
} sort(p+1,p+n+1);
for(int i=1,r=-1;i<=n;i++)if(p[i].r>r)r=p[i].r,p[++c]=p[i]; n=c;
for(int i=1;i<n;i++)if(p[i].r>=p[i+1].l)g[i]=sq(p[i].r-p[i+1].l+1);
for(int i=1;i<=n;i++)p[i].r++;
ll l=-1e12,r=0;
while(l<r)K=(l+r>>1)+1,check()?(l=K,ans=f[n]+K*k):r=K-1;
cout<<ans<<endl;
return 0;
}
P5896 [IOI2016]aliens的更多相关文章
- DP 优化方法大杂烩 & 做题记录 I.
标 * 的是推荐阅读的部分 / 做的题目. 1. 动态 DP(DDP)算法简介 动态动态规划. 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 ...
- Uva12206 Stammering Aliens 后缀数组&&Hash
Dr. Ellie Arroway has established contact with an extraterrestrial civilization. However, all effort ...
- HDU4080 Stammering Aliens(二分 + 后缀数组)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4080 Description Dr. Ellie Arroway has establish ...
- UVA 12206 - Stammering Aliens(后缀数组)
UVA 12206 - Stammering Aliens 题目链接 题意:给定一个序列,求出出现次数大于m,长度最长的子串的最大下标 思路:后缀数组.搞出height数组后,利用二分去查找就可以 这 ...
- Stammering Aliens
Stammering Aliens Time Limit: 2000MS Memory Limit: 65536K Description Dr. Ellie Arroway has ...
- XCOM2中敌对生物设计分析(Aliens篇)
Aliens Aliens作为游戏设定中入侵的外星人,有各式外貌及奇特的战斗方式,掌握一些高能科技或利用精神力量进行攻击 Sectoid 使用灵能战斗的外星人,并无高级版本,初级便会使用精神控制,生命 ...
- UVA10570-Meeting with Aliens(枚举)
Problem UVA1616-Caravan Robbers Accept: 531 Submit: 2490Time Limit: 3000 mSec Problem Description I ...
- 【UOJ#236】[IOI2016]railroad(欧拉回路,最小生成树)
[UOJ#236][IOI2016]railroad(欧拉回路,最小生成树) 题面 UOJ 题解 把速度看成点,给定的路段看成边,那么现在就有了若干边,然后现在要补上若干边,以及一条\([inf,\) ...
- UVA-10570 Meeting with Aliens (枚举+贪心)
题目大意:将一个1~n的环形排列变成升序的,最少需要几次操作?每次操作可以交换任意两个数字. 题目分析:枚举出1的位置.贪心策略:每次操作都保证至少一个数字交换到正确位置上. # include< ...
随机推荐
- 对比7种分布式事务方案,还是偏爱阿里开源的Seata,真香!(原理+实战)
前言 这是<Spring Cloud 进阶>专栏的第六篇文章,往期文章如下: 五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强? openFeign夺命连环9问,这谁受得了? 阿里面 ...
- Kruskal重构树-进阶
例题一:区间最小生成树(NKOJ P8439) 简要题意: 一个n个点m条边的无向图,点编号1到n,边编号1到m.边有边权. 有q次操作,操作分两种: 1.k x y z:修改第k条边,使其连接的两点 ...
- [HNOI2009]双递增序列(洛谷P4728)+小烈送菜(内部训练题)——奇妙的dp
博主学习本题的经过嘤嘤嘤: 7.22 : 听学长讲(一知半解)--自己推(推不出来)--网上看题解--以为自己会了(网上题解是错的)--发现错误以后又自己推(没推出来)--给学长发邮件--得到正确解法 ...
- python环境搭建、pycharm安装
一. 实验目标 (1) Python环境搭建 (2) 会pycharm安装和使用 (3) 了解python程序设计流程 二. 实验内容 1.勾选Add Python 3.7 ...
- WPF进阶技巧和实战03-控件(4-基于范围的控件及日期控件)
系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...
- Windows内核中的CPU架构-6-中断门(32-Bit Interrupt Gate)
Windows内核中的CPU架构-6-中断门(32-Bit Interrupt Gate) 中断门和调用门类似,也是一种系统段.同样的它也可以用来提权. 中断门: 虽然中断门的段描述符如下: 但是中断 ...
- 攻防世界 WEB 高手进阶区 TokyoWesterns CTF shrine Writeup
攻防世界 WEB 高手进阶区 TokyoWesterns CTF shrine Writeup 题目介绍 题目考点 模板注入 Writeup 进入题目 import flask import os a ...
- js之变量与数据类型
变量 声明 一个变量被重新复赋值后,它原有的值就会被覆盖,变量值将以最后一次赋的值为准. var age = 18; age = 81; // 最后的结果就是81因为18 被覆盖掉了 同时声明多个变量 ...
- [python]selenium常用的操作
浏览器 1.火狐浏览器 br = webdriver.Firefox() #最大化窗口br.maximize_window() br.get('http://baidu.com') 2.谷歌浏览器 b ...
- 2021 羊城杯WriteUP
比赛感受 题目质量挺不错的,不知道题目会不会上buu有机会复现一下,躺了个三等奖,发下队伍的wp Team BinX from GZHU web Checkin_Go 源码下载下来发现是go语言写的 ...