斜率优化

首先,可以进行斜率优化的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. 094 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 04 static关键字(续)

    094 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  2. >>8) & 0xFF中的 >> 和 &0xFF 的作用

    参考:https://blog.csdn.net/iamgamer/article/details/79354617 其中有两个位运算,一个是>>,一个是&. 0xff的作用一: ...

  3. 《C++primerplus》第12章练习题

    做一下倒数两题,都是在队列模拟的程序基础上做点修改测试. 5.找出平均等候时间为1分钟时,每小时到达的客户数为多少(试验时间不少于100小时). 指定队伍最大长度10人,模拟100小时.粗略估计答案在 ...

  4. 使用Appium进行iOS的真机自动化测试

    windows不支持appium连接ios,只适用于mac 使用Appium进行iOS的真机自动化测试 安装类库 Homebrew 如果没有安装过Homebrew,先安装[ homebrew ] np ...

  5. [学习笔记] Treap

    想必大家都知道一种叫做二叉搜索树这东西吧,那么我们知道,在某些特殊情况下,二叉搜索树会退化成一条链,而且如果出题人成心想卡你的话也很简单,分分钟把你(n log n)的期望卡成.那么我们该如何避免这种 ...

  6. js 递归的理解

    友情提示:阅读本文需花 3分钟左右! 递归函数必须接受参数. (比如我要递归谁?) 在递归函数的定义初始,应该有一个判断条件,当参数满足这个条件的时候,函数停止执行,并返回值.(指定退出条件,否则就会 ...

  7. 多测师讲解 自动化测试理论(1)_高级讲师肖sir

    自动化测试理论什么是自动化测试?广义的:通过工具或程序替代或辅助人工测试的行为叫自动化测试狭义的:通过工具录制或编写脚本模拟手工测试的过程,通过回放或运行脚本执行测试用例,从而代替人工对系统的功能验证 ...

  8. 学习go语言并完成第一个作品

    之前有使用C#写一个Windows下的发送邮件的命令行工具,方便一些脚本出现异常时向我的邮箱发送邮件提醒.但这并没有被我频繁使用,因为我的有些脚本还是在linux下面运行,因此我又有一篇文章用linu ...

  9. python实现elasticsearch操作-CRUD API

    python操作elasticsearch常用API 目录 目录 python操作elasticsearch常用API1.基础2.常见增删改操作创建更新删除3.查询操作查询拓展类实现es的CRUD操作 ...

  10. logstash-配置文件详解

      kafka  将 kafka topic 中的数据读取为事件   kafka{ bootstrap_servers=> "kafka01:9092,kafka02:9092,kaf ...