斜率优化

首先,可以进行斜率优化的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. 一文搞懂AQS及其组件的核心原理

    @ 目录 前言 AbstractQueuedSynchronizer Lock ReentrantLock 加锁 非公平锁/公平锁 lock tryAcquire addWaiter acquireQ ...

  2. 使用 Aria2 代替迅雷

    一.原因 迅雷下载速度一般,thunder:// 开头的链接也逐渐被 bt 链接替代. 迅雷很流氓,安装后 (尤其是 Windows 系统) 浏览器默认使用迅雷下载,对于小文件来说使用浏览器内置下载可 ...

  3. 017 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 11 变量综合案例

    017 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 11 变量综合案例 本文知识点:变量 相同类型的变量可以一次同时定义多个 例:可以一行代码同时定义2个变量x ...

  4. C1853 编译器错误:fatal error C1853: 'pjtname.pch' precompiled header file is from a previous

    转载:https://www.cnblogs.com/emanlee/archive/2010/10/16/1852998.html 用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件 ...

  5. P5322 排兵布阵解题报告

    本想在洛谷上交篇题解的,结果发现交不了,所以只能在这边写了... 作为一个蒟蒻,看到省选题,第一眼考虑怎么打暴力 我们可以分情况考虑 当\(s==1\)的时候 我们可以把他当成一个\(01\)背包,背 ...

  6. Appium自动化测试之环境安装

    安装前准备: Python 安装包下载       选择想要想在的python包Node-v6.11.2下载安卓SDK下载appium_forwindows下载 以上四个文件下载下来后,分别解压安装, ...

  7. vue拼图动画Demo

    这是一个基于vue的Demo,可以实现拼图动画,但是具体的没有写拼图成功的逻辑,鼠标悬停移动.周期刷新 我把它放到的我的博客园界面上了.刷新界面可以看到. 演示地址 :https://liruilon ...

  8. 字节码暴力破解censum(老版本)

    声明 事先声明,本文仅提供破解方法以供个人及读者们学习Java字节码,不提倡破解程序. 本文是个人学习掘金小册张师傅的<JVM字节码从入门到精通>后,作为一个实践的记录,并无恶意. 关于c ...

  9. Fabric1.4.4 多机solo共识搭建

    简单记录一下fabric版本1.4.4的搭建部署,运行环境为CentOs7.8,如有错误欢迎批评指正.然后就是本编文章不对环境搭建做说明,基础环境搭建看这里. 1.总体设计 本案例部署一个排序(ord ...

  10. JS关闭chorme页面

    百度到的很多答案都失效了,这是收集一位博主的(https://www.jianshu.com/p/9dc2752194b8),目前可以使用. 代价是打开一个空白页面,能实现无提示关闭当前页面.不需要是 ...