写在前面

这个东西应该是一个非常重要的套路......所以我觉得必须写点什么记录一下,免得自己忘掉了

一直以来我的斜率dp都掌握的不算很好......也很少主动地在比赛里想到

写这个的契机是noi.ac在今天的考试中考了一道用这玩意儿的原题,被我搞出来了,于是决定总结一下(毕竟见得越来越多)

斜率dp

考虑一个常见的二次复杂度的dp:

$dp[i]=min(dp[j]+c(i)+g(j)+k(i)*f(j))$

其中$c,g,k,f$都是只和括号里的$i,j$有关的一元函数

一个很重要的思想是:看到n方dp的时候先想想能不能搞成这个样子的式子

如果搞出来了,这个东西一定可以在$O(n\log n)$的时间里面做出来——用cdq分治

怎么cdq

我们先给这四个函数名字:

$c(i)$是额外附加的只和$i$有关的常数

$f(i)=x(i)$作为横坐标

$g(i)=y(i)$作为纵坐标

$k(i)$是$i$这一点上的转移斜率

首先把所有点按照斜率排序

对于过程solve(l,r),这样操作:

首先,按照输入编号,把(l,r)分成两半,然后递归处理solve(l,mid)

返回的是一个按照横坐标排好序的原数组(dp值都知道了的)

我们把这一批东西做一个上凸包(或者下凸包,依照要求max还是min变化)

然后对于后面那一半点我们用前面这个凸包更新答案,一个指针遍历右边一半,另一个指针遍历左边的凸包,每次跳到最优位置为止

这之后,我们递归处理右半部分

最后我们再对这两半归并排序,按照横坐标

什么意义?

实际上这一波操作中,有三个中间被我们排了序的元素:输入编号,斜率,横坐标

实际上就是一个三维偏序:因为不像普通的斜率dp那样横纵坐标或者斜率有单调性,所以我们强行cdq

这样,在每一次更新后一半的时候,前一半都是做完的,而且已经横坐标单调了

例题:2019.3.16 problemB

朴素n方dp很好看出来,然后发现可以直接套到上面式子里面

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define head DEEP_DARK_FANTASY
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n;;
struct node{
ll w,h,c,x,y,k,dp,num;
}a[100010],tmp[100010],q[100010];
inline bool cmp1(node l,node r){
return l.k<r.k;
}
void solve(int l,int r){
if(l==r){
a[l].x=a[l].h;
a[l].y=a[l].dp-a[l].w+a[l].h*a[l].h;
return;
}
int mid=(l+r)>>1,tl,tr,head,tail,i; tl=tr=0;
for(i=l;i<=r;i++){
if(a[i].num<=mid) tmp[++tl]=a[i];
else q[++tr]=a[i];
}
for(i=l;i<=mid;i++) a[i]=tmp[i-l+1];
for(i=mid+1;i<=r;i++) a[i]=q[i-mid]; solve(l,mid); head=1,tail=0;
for(i=l;i<=mid;i++){
while(tail>head&&(q[tail].y-q[tail-1].y)*(a[i].x-q[tail].x)>=(q[tail].x-q[tail-1].x)*(a[i].y-q[tail].y)) tail--;
q[++tail]=a[i];
} tl=1;
for(i=mid+1;i<=r;i++){
while(tl<tail&&a[i].k*(q[tl+1].x-q[tl].x)>=(q[tl+1].y-q[tl].y)) tl++;
a[i].dp=min(a[i].dp,-q[tl].x*a[i].k+q[tl].y+a[i].c);
} solve(mid+1,r); tl=l;tr=mid+1;head=0;
while(tl<=mid&&tr<=r){
if(a[tl].x==a[tr].x) tmp[++head]=((a[tl].y>a[tr].y)?a[tr++]:a[tl++]);
else tmp[++head]=((a[tl].x>a[tr].x)?a[tr++]:a[tl++]);;
}
while(tl<=mid) tmp[++head]=a[tl++];
while(tr<=r) tmp[++head]=a[tr++];
for(i=l;i<=r;i++) a[i]=tmp[i-l+1];
}
int main(){
n=read();int i;
for(i=1;i<=n;i++){
a[i].h=read();
a[i].dp=1e18;
a[i].num=i;
}
for(i=1;i<=n;i++){
a[i].w=read();
a[i].w+=a[i-1].w;
}
for(i=1;i<=n;i++){
a[i].c=a[i].h*a[i].h+a[i-1].w;
a[i].k=2ll*a[i].h;
}
a[1].dp=0;
sort(a+1,a+n+1,cmp1);
solve(1,n);
for(i=1;i<=n;i++)
if(a[i].num==n) printf("%lld\n",a[i].dp);
}

斜率dp+cdq分治的更多相关文章

  1. 斜率dp cdq 分治

    f[i] = min { f[j] + sqr(a[i] - a[j]) } f[i]= min { -2 * a[i] * a[j] + a[j] * a[j] + f[j] } + a[i] * ...

  2. bzoj 2726 [SDOI2012]任务安排(斜率DP+CDQ分治)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2726 [题意] 将n个任务划分成若干个块,每一组Mi任务花费代价(T+sigma{ t ...

  3. bzoj 1492 [NOI2007]货币兑换Cash(斜率dp+cdq分治)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1492   [题意] 有AB两种货币,每天可以可以付IPi元,买到A券和B券,且A:B= ...

  4. 【bzoj3672】[Noi2014]购票 斜率优化dp+CDQ分治+树的点分治

    题目描述  给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$.问从每个点到1花费 ...

  5. bzoj 2244 [SDOI2011]拦截导弹(DP+CDQ分治+BIT)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2244 [题意] 给定n个二元组,求出最长不上升子序列和各颗导弹被拦截的概率. [思路] ...

  6. BZOJ_3963_[WF2011]MachineWorks_斜率优化+CDQ分治

    BZOJ_3963_[WF2011]MachineWorks_斜率优化+CDQ分治 Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM) ...

  7. 【BZOJ-1492】货币兑换Cash DP + 斜率优化 + CDQ分治

    1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 3396  Solved: 1434[Submit][Sta ...

  8. 洛谷.4655.[CEOI2017]Building Bridges(DP 斜率优化 CDQ分治)

    LOJ 洛谷 \(f_i=s_{i-1}+h_i^2+\min\{f_j-s_j+h_j^2-2h_i2h_j\}\),显然可以斜率优化. \(f_i-s_{i-1}-h_i^2+2h_ih_j=f_ ...

  9. BZOJ.1492.[NOI2007]货币兑换(DP 斜率优化 CDQ分治/Splay)

    BZOJ 洛谷 如果某天能够赚钱,那么一定会在这天把手上的金券全卖掉.同样如果某天要买,一定会把所有钱花光. 那么令\(f_i\)表示到第\(i\)天所拥有的最多钱数(此时手上没有任何金券),可以选择 ...

随机推荐

  1. 【vlan之四种方式链路认证组网]

    ---恢复内容开始--- 根据项目需求,搭建好如下拓扑图: 在[sysname]下配置给予协议的vlan vlan 1#vlan 10 protocol-vlan 0 ipv4#vlan 20 pro ...

  2. laravel -- 路由

    基本路由 Route::get('/get',function (){ return "this is get"; }); Route::post('/post',function ...

  3. python 连接MSSQL

    # -*- coding: utf-8 -*- import pymssql conn=pymssql.connect(host=".",user="sa",p ...

  4. python中使用空格还是使用 Tab键缩进的建议

    对于程序员来说,其实Tab和空格远远不只是“立场”问题那么简单. 在不同的编辑器里tab的长度可能不一致,所以在一个编辑器里用tab设置缩进后,在其它编辑器里看可能缩进就乱了.空格不会出现这个问题,因 ...

  5. 贪心算法之Huffman

    Huffman编码,权重越大,离根节点越大.所以就是不断的选取两个最小的树,然后组成一颗新树,加入集合,然后去除已选的两棵树.不断的循环,直到最后的树的集合只剩下一棵,则构建完成,最后输出Huffma ...

  6. win7 下安装oracle 11g出现错误: 启动服务出现错误 找不到服务OracleMTSRecoveryService

    这种错误是在多次安装oracle都没有成功的情况下发生的. 正确安装oracle,是有前提条件的 1,安装最新的jdk,不是jre!!(并配好环境变量,在cmd中测试 java -version与ja ...

  7. 两种方法实现Python二分查找算法

    两种方法实现Python二分查找算法   一. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 arr=[1,3,6,9,10,20,30] def findnumber( ...

  8. 「日常训练」「小专题·USACO」 Broken Necklace(1-2)

    题意 圆形链条,打断一处可以形成一条链.问在哪个地方开始打断,能够形成最大的连续颜色(白色视作同样的颜色)? 分析 说起来很高级,但是我们实际上并不需要穷举打断的地方,只需要把串重复三回啊三回.然后从 ...

  9. jmeter完成CAS登录,并获取token(原创)

    思路: 1.系统完成CAS登录需要验证用户名/密码,以及动态授权参数 2.先通过指定url用正则提取出动态授权参数 3.完成登录需要cookie,需用正则提取出对应的cookie,已完成参数化的自动登 ...

  10. jmeter之HTTP请求

    1.添加一个线程组:Test plan_添加_Threads(users)_线程组(右键操作),如下图: 2.添加一个HTTP请求:线程组_添加_sample_HTTP请求(右键操作),如下图: 3. ...