斜率优化

首先,可以进行斜率优化的DP方程式一般式为$dp[i]=\max_{j=1}^{i-1}/\min_{j=1}^{i-1}\{a(i)*x(j)+b(i)*y(j)\}$

其中$a(j)$和$b(j)$都是关于$j$的函数,在$O(1)$时间内可以计算得出

将方程式进行变形

$$dp[i]=a(i)*x(j)+b(i)*y(j)$$

$$dp[i]-a(i)*x(j)=b(i)*y(j)$$

$$y(j)=-\frac{a(i)}{b(i)}x(j)+\frac{dp[i]}{b(i)}$$

我们可以称$y=-\frac{a(i)}{b(i)}x+\frac{dp[i]}{b(i)}$为$i$的特征直线

那么对于$i$这个决策点来说,在$i$之前所有决策点$j$($j<i$)可以看作一个二维平面上的点,横坐标为$x(j)$,纵坐标为$y(j)$,那么i在寻找最优决策点的过程就是用i的特征直线去截平面上的每一个点,求出截距,找到最大/最小的$dp[i]$

但如果直接去做复杂度为$O(n^{2})$

那么可以通过维护在平面上维护凸包(根据具体的斜率正负和$b(i)$的正负决定维护上凸包还是下凸包),直线在平移过程中切到凸包的第一个点就是当前i的最优决策点,那么时间复杂度均摊$O(n)$

以$-\frac{a(i)}{b(i)}>0$,$b(i)>0$,$dp$取最小值为例

直线$EF$不断从截距无限小向上平移,直到截到凸包上的点

那么需要做的是维护凸包

1.若$x(i)$单调,斜率$-\frac{a(i)}{b(i)}$单调

所以在平面上的点是按顺序依次排列,去截的特征直线的斜率不断增加或减少

可以发现特征直线截到凸包上的第一个点记为$p$,那么$p$左边的点和$p$的直线的斜率$k_{1}$,$p$右边的点和$p$的直线的斜率$k_{2}$,特征直线的斜率一定介于$k_{1}$和$k_{2}$之间

那么可以利用单调队列来维护平面上的点的凸包,然后在单调队列队首不断维护当前特征直线的斜率,找到第一个大于或小于(根据凸包是上凸包还是下凸包决定)的决策点

时间复杂度$O(n)$

2.若$x(i)$单调,斜率不单调

仍然维护凸包,但此时特征直线的斜率没有规律,利用上面的结论可以二分凸包上两点的斜率,来找到第一个切到的点

时间复杂度$O(nlogn)$

3.若$x(i)$不单调,斜率不单调

对无规律的三个维度进行CDQ分治

具体见货币兑换的题解

#include <bits/stdc++.h>
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
#define eps 1e-9
using namespace std;
const int N=100100;
int n,h,t,q[N];
double s,dp[N];
struct node
{
double a,b,r,k,x,y;
int id;
}sh[N];
node p[N];
bool cmp(node a,node b)
{
return a.k>b.k;
}
double slope(int i,int j)
{
if (sh[j].x==sh[i].x) return 1e9;
return (sh[j].y-sh[i].y)/(sh[j].x-sh[i].x);
}
void cdq(int l,int r)
{
if (l==r)
{
dp[l]=max(dp[l],dp[l-1]);
sh[l].x=sh[l].r*dp[l]/(sh[l].a*sh[l].r+sh[l].b);
sh[l].y=dp[l]/(sh[l].a*sh[l].r+sh[l].b);
return;
}
int mid=(l+r)>>1,tl=l-1,tr=mid;
for (int i=l;i<=r;++i)
{
if (sh[i].id<=mid) p[++tl]=sh[i];
else p[++tr]=sh[i];
}
for (int i=l;i<=r;i++) sh[i]=p[i];
cdq(l,mid);
h=0;t=-1;
for (int i=l;i<=mid;++i)
{
while (h<t && slope(q[t],q[t-1])<slope(q[t],i)) t--;
q[++t]=i;
}
for (int i=mid+1;i<=r;++i)
{
while (h<t && sh[i].k<slope(q[h],q[h+1])) h++;
int j=q[h];
dp[sh[i].id]=max(dp[sh[i].id],sh[i].a*sh[j].x+sh[i].b*sh[j].y);
}
cdq(mid+1,r);
tl=l;tr=mid+1;
int cnt=l-1;
while (tl<=mid && tr<=r)
{
if (sh[tl].x-sh[tr].x<eps) p[++cnt]=sh[tl],tl++;
else p[++cnt]=sh[tr],tr++;
}
for (int i=tl;i<=mid;++i) p[++cnt]=sh[i];
for (int i=tr;i<=r;++i) p[++cnt]=sh[i];
for (int i=l;i<=r;++i) sh[i]=p[i];
}
int main()
{
scanf("%d%lf",&n,&s);
for (int i=1;i<=n;i++)
{
scanf("%lf%lf%lf",&sh[i].a,&sh[i].b,&sh[i].r);
sh[i].k=-sh[i].a/sh[i].b;sh[i].id=i;
}
sort(sh+1,sh+1+n,cmp);
dp[0]=s;
cdq(1,n);
printf("%.3lf\n",dp[n]);
}

时间复杂度$O(nlogn)$

若在将序列上的问题转化到树上则利用点分治见购票

#include <bits/stdc++.h>
#define inf 1e18
#define int long long
#define re register
using namespace std;
const int N=2*1e5+100;
int n,T,sum[N],dp[N];
int h,t,q[N],sz[N],root,vi[N];
int tot,first[N],nxt[N*2],point[N*2];
int son[N],w;
struct node
{
int fa,s,p,q,l;
}sh[N];
void add_edge(int x,int y)
{
tot++;
nxt[tot]=first[x];
first[x]=tot;
point[tot]=y;
}
bool cmp(int a,int b)
{
return sum[a]-sh[a].l>sum[b]-sh[b].l;
}
double slope(int i,int j)
{
return 1.0*(dp[j]-dp[i])/(1.0*(sum[j]-sum[i]));
}
int find(int l,int r,double k)
{
if (r<l) return q[h];
if (slope(q[l],q[l+1])>=k) return q[l];
while (l<r)
{
int mid=l+((r-l+1)>>1);
if (slope(q[mid],q[mid+1])<k) l=mid;
else r=mid-1;
}
return q[l+1];
}
void dfs(int x)
{
sz[x]=1;
for (re int i=first[x];i!=-1;i=nxt[i])
{
int u=point[i];
if (u==sh[x].fa) continue;
sum[u]=sum[x]+sh[u].s;
dfs(u);
sz[x]+=sz[u];
}
}
void dfs_sz(int x,int fa)
{
sz[x]=1;
for (re int i=first[x];i!=-1;i=nxt[i])
{
int u=point[i];
if (u==fa || vi[u]) continue;
dfs_sz(u,x);
sz[x]+=sz[u];
}
}
void dfs_rt(int x,int fa,int tot)
{
bool bl=1;
for (re int i=first[x];i!=-1;i=nxt[i])
{
int u=point[i];
if (u==fa || vi[u]) continue;
dfs_rt(u,x,tot);
if (sz[u]>tot/2) bl=0;
}
if (tot-sz[x]>tot/2) bl=0;
if (bl) root=x;
}
void dfs_insert(int x,int fa)
{
son[++w]=x;
for (re int i=first[x];i!=-1;i=nxt[i])
{
int u=point[i];
if (u==fa || vi[u]) continue;
dfs_insert(u,x);
}
}
void divide(int x)
{
vi[x]=1;
vector <int> father;
father.push_back(x);
if (sh[x].fa && !vi[sh[x].fa])
{
for (int i=sh[x].fa;i!=0 && !vi[i];i=sh[i].fa)
father.push_back(i);
dfs_sz(sh[x].fa,x);
dfs_rt(sh[x].fa,x,sz[sh[x].fa]);
divide(root);
for (re int i=1;i<(int)father.size();++i)
if (sum[father[i]]>=sum[x]-sh[x].l)
dp[x]=min(dp[x],dp[father[i]]+(sum[x]-sum[father[i]])*sh[x].p+sh[x].q);
}
w=0;
for (re int i=first[x];i!=-1;i=nxt[i])
{
int u=point[i];
if (u==sh[x].fa || vi[u]) continue;
dfs_insert(u,x);
}
sort(son+1,son+1+w,cmp);
h=n+1;t=n;
for (re int i=1,j=0;i<=w;++i)
{
int u=son[i];
while (j<(int)father.size() && sum[father[j]]>=sum[u]-sh[u].l)
{
while (h<t && slope(q[h],q[h+1])<slope(q[h],father[j])) h++;
q[--h]=father[j];
j++;
}
if (h>t) continue;
int pos=find(h,t-1,1.0*sh[u].p);
dp[u]=min(dp[u],dp[pos]+(sum[u]-sum[pos])*sh[u].p+sh[u].q);
}
for (re int i=first[x];i!=-1;i=nxt[i])
{
int u=point[i];
if (u==sh[x].fa || vi[u]) continue;
dfs_sz(u,x);
dfs_rt(u,x,sz[u]);
divide(root);
}
}
signed main()
{
tot=-1;
memset(first,-1,sizeof(first));
memset(nxt,-1,sizeof(nxt));
scanf("%lld%lld",&n,&T);
for (re int i=2;i<=n;++i)
{
scanf("%lld%lld%lld%lld%lld",&sh[i].fa,&sh[i].s,&sh[i].p,&sh[i].q,&sh[i].l);
add_edge(sh[i].fa,i);
add_edge(i,sh[i].fa);
}
sh[1].fa=0;dp[1]=0;
for (re int i=2;i<=n;++i) dp[i]=inf;
dfs(1);
dfs_rt(1,0,sz[1]);
divide(root);
for (re int i=2;i<=n;++i) printf("%lld\n",dp[i]);
}

DP斜率优化学习笔记的更多相关文章

  1. hdu3507 斜率优化学习笔记(斜率优化+dp)

    QWQ菜的真实. 首先来看这个题. 很显然能得到一个朴素的\(dp\)柿子 \[dp[i]=max(dp[i],dp[j]+(sum[i]-sum[j])^2) \] 但是因为\(n\le 50000 ...

  2. 【HNOI2008】玩具装箱TOY & 斜率优化学习笔记

    题目 P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为 \(1\cdots N\ ...

  3. dp斜率优化

    算法-dp斜率优化 前置知识: 凸包 斜率优化很玄学,凭空讲怎么也讲不好,所以放例题. [APIO2014]序列分割 [APIO2014]序列分割 给你一个长度为 \(n\) 的序列 \(a_1,a_ ...

  4. 【BZOJ-4518】征途 DP + 斜率优化

    4518: [Sdoi2016]征途 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 230  Solved: 156[Submit][Status][ ...

  5. 【BZOJ-3437】小P的牧场 DP + 斜率优化

    3437: 小P的牧场 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 705  Solved: 404[Submit][Status][Discuss ...

  6. 【BZOJ-1010】玩具装箱toy DP + 斜率优化

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 8432  Solved: 3338[Submit][St ...

  7. 【BZOJ】1096: [ZJOI2007]仓库建设(dp+斜率优化)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1096 首先得到dp方程(我竟然自己都每推出了QAQ)$$d[i]=min\{d[j]+cost(j+ ...

  8. BZOJ 1096: [ZJOI2007]仓库建设(DP+斜率优化)

    [ZJOI2007]仓库建设 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在 ...

  9. 学渣乱搞系列之dp斜率优化

    学渣乱搞系列之dp斜率优化 By 狂徒归来 貌似dp的斜率优化一直很难搞啊,尤其是像我这种数学很挫的学渣,压根不懂什么凸包,什么上凸下凸的,哎...说多了都是泪,跟wdd讨论了下,得出一些结论.本文很 ...

随机推荐

  1. 【题解】[APIO2010]特别行动队

    Link 题目大意:一段区间的贡献是\(ax^2+bx+c,x=\sum v\),求一个划分让总区间的价值最大.分段必须连续. \(\text{Solution:}\) 设计\(dp[i]\)表示前\ ...

  2. Android高级控件(下)

    计时器(Chronometer) getBase() 基准时间 setFormat() 设置显示格式 start() 开始计时 stop() 停止计时 setOnChronometerListener ...

  3. 【原创】经验分享:一个小小emoji尽然牵扯出来这么多东西?

    前言 之前也分享过很多工作中踩坑的经验: 一个线上问题的思考:Eureka注册中心集群如何实现客户端请求负载及故障转移? [原创]经验分享:一个Content-Length引发的血案(almost.. ...

  4. asp.net mvc核心、实体框架和simplepagin .js中的分页

    下载demo - 516.1 KB , 介绍 这篇文章将解释如何在asp.net mvc核心应用程序中进行分页,目标是enity框架,并使用jquery模板simplepagin .js. 我的一个应 ...

  5. Java泛型的协变与逆变

    泛型擦除 Java的泛型本质上不是真正的泛型,而是利用了类型擦除(type erasure),比如下面的代码就会出现错误: 报的错误是:both methods  have same erasure ...

  6. CentOS7 没有安装 ifconfig 命令

    ifconfig 命令是设置或显示网络接口的程序,可以显示出我们机器的网卡信息. 除此之外, ip a 命令,也可以设置或显示网卡的信息 在 CentOS 7 下,默认 ifconfig 命令是没有安 ...

  7. Hadoop框架:NameNode工作机制详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.存储机制 1.基础描述 NameNode运行时元数据需要存放在内存中,同时在磁盘中备份元数据的fsImage,当元数据有更新或者添加元数据 ...

  8. Java防止文件被篡改之文件校验和

    Java防止文件被篡改之文件校验和转载:请注明出处,谢谢! 1.为什么要防止文件被篡改?  答案是显然的,为了保证版权,系统安全性等.之前公司开发一个系统,技术核心是一个科学院院士的研究成果,作为一款 ...

  9. 多测试讲解_009肖sirRF自动化框架安装教程

    robot framework:自动化测试框架 Python3.7 RIDE(可视化界面).  Wxpython  pip(在线下载) . setuptools(在线安装) . 第三方库 第三方库:s ...

  10. rs485转以太网转换器

    rs485转以太网转换器ZLAN5103 实现RS485转以太网(即485转网口)主要一个硬件转换器和一个软件驱动.硬件转换器分为两种:串口服务器(串口联网服务器.串口通信服务器).串口联网模块.RS ...