洛谷 P3994 高速公路
https://www.luogu.org/problemnew/show/P3994
设dp[i] 表示第i个城市到根节点的最小花费
dp[i]=min{ (dis[i]-dis[j])*P[i]+Q[i]+dp[j] }
这是O(n^2)的
这个式子可以斜率优化
dp[i]+dis[j]*P[i]=dis[i]*P[i]+Q[i]+dp[j]
就是一条斜率为P[i]的直线,截(dis[j],dp[j])的最小截距
在根往下走的过程中,斜率单调递增
这就体现了 为什么题目中说“i号城市是j号城市的某个祖先,那么一定存在Pi<=Pj”
我们按dfs序dp
现在唯一的问题就是如何得到 一个点到根节点路径上的单调队列
只需要考虑如何去除兄弟节点的子树对单调队列的影响
即在一个节点退出dfs时,将单调队列恢复为这个节点开始dfs的情况
头指针只是不断的+1,没有涉及到单调队列中元素的修改,所以记录下头指针在哪个位置即可
尾指针涉及到元素的替换,但是它只会替换一个元素,所以记录下尾指针的位置,以及被当前点替换的元素是谁
当节点退出dfs时,恢复记录的这三个值即可
这样的话,一个节点多次出队入队,时间复杂度就不是O(n)了
所以二分出队位置,时间复杂度为O(nlogn)
朴素的DP:
#include<cstdio>
#include<iostream>
#include<algorithm> using namespace std; #define N 1000001 typedef long long LL; int P[N],Q[N]; int front[N],to[N<<],nxt[N<<],val[N<<],tot; int fa[N]; LL dis[N]; int t;
LL mi[N]; void read(int &x)
{
x=; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
} void add(int u,int v,int w)
{
to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=w;
} void dfs(int x,int f)
{
for(int i=front[x];i;i=nxt[i])
{
if(to[i]==f) continue;
dis[to[i]]=dis[x]+val[i];
mi[to[i]]=dis[to[i]]*P[to[i]]+Q[to[i]];
t=fa[to[i]];
while(t!=)
{
mi[to[i]]=min(mi[to[i]],(dis[to[i]]-dis[t])*P[to[i]]+Q[to[i]]+mi[t]);
t=fa[t];
}
dfs(to[i],x);
}
} int main()
{
int n,s;
read(n);
for(int i=;i<n;++i)
{
read(fa[i+]); read(s); read(P[i+]); read(Q[i+]);
add(fa[i+],i+,s);
}
dfs(,);
for(int i=;i<=n;++i) cout<<mi[i]<<'\n';
}
斜率优化,暴力出队:
#include<cstdio>
#include<iostream> using namespace std; #define N 1000001 typedef long long LL; int front[N],nxt[N],to[N],tot,val[N]; int P[N],Q[N]; int q[N],head,tail; LL dis[N];
LL dp[N]; void read(int &x)
{
x=; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
} void add(int u,int v,int w)
{
to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
} inline double X(int i,int j) { return dis[j]-dis[i]; }
inline double Y(int i,int j) { return dp[j]-dp[i]; } void dfs(int x)
{
int now_h=head,now_t=tail;
while(head<tail- && Y(q[head],q[head+])<P[x]*X(q[head],q[head+])) head++;
int j=q[head];
dp[x]=(dis[x]-dis[j])*P[x]+dp[j]+Q[x];
while(head<tail- && Y(q[tail-],q[tail-])*X(q[tail-],x)>X(q[tail-],q[tail-])*Y(q[tail-],x)) tail--;
int rr=q[tail];
q[tail++]=x;
for(int i=front[x];i;i=nxt[i])
dis[to[i]]=dis[x]+val[i],dfs(to[i]);
head=now_h; q[tail-]=rr; tail=now_t;
} int main()
{
int n;
read(n);
int fa,d;
for(int i=;i<=n;++i)
{
read(fa); read(d);
add(fa,i,d);
read(P[i]); read(Q[i]);
}
for(int i=front[];i;i=nxt[i])
{
dis[to[i]]=val[i];
q[head=]=; tail=;
dfs(to[i]);
}
for(int i=;i<=n;++i) cout<<dp[i]<<'\n';
}
斜率优化,二分出队
#include<cstdio>
#include<iostream> using namespace std; #define N 1000001 typedef long long LL; int front[N],nxt[N],to[N],tot,val[N]; int P[N],Q[N]; int q[N],head,tail; LL dis[N];
LL dp[N]; void read(int &x)
{
x=; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
} void add(int u,int v,int w)
{
to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
} inline double X(int i,int j) { return dis[j]-dis[i]; }
inline double Y(int i,int j) { return dp[j]-dp[i]; } void dfs(int x)
{
int now_h=head,now_t=tail;
int l=head,r=tail-,mid,tmp=-;
while(l<=r)
{
mid=l+r>>;
if(Y(q[mid],q[mid+])>=P[x]*X(q[mid],q[mid+])) tmp=mid,r=mid-;
else l=mid+;
}
if(tmp!=-) head=tmp;
else head=tail-;
int j=q[head];
dp[x]=(dis[x]-dis[j])*P[x]+dp[j]+Q[x];
l=head,r=tail-,tmp=-;
while(l<=r)
{
mid=l+r>>;
if(Y(q[mid],q[mid+])*X(q[mid+],x)<=X(q[mid],q[mid+])*Y(q[mid+],x)) tmp=mid,l=mid+;
else r=mid-;
}
if(tmp!=-) tail=tmp+;
else tail=head+;
int rr=q[tail];
q[tail++]=x;
for(int i=front[x];i;i=nxt[i])
dis[to[i]]=dis[x]+val[i],dfs(to[i]);
head=now_h; q[tail-]=rr; tail=now_t;
} int main()
{
int n;
read(n);
int fa,d;
for(int i=;i<=n;++i)
{
read(fa); read(d);
add(fa,i,d);
read(P[i]); read(Q[i]);
}
for(int i=front[];i;i=nxt[i])
{
dis[to[i]]=val[i];
q[head=]=; tail=;
dfs(to[i]);
}
for(int i=;i<=n;++i) cout<<dp[i]<<'\n';
}
洛谷 P3994 高速公路的更多相关文章
- 洛谷 P3994 高速公路(斜率优化)
题目链接 题意:给出一棵树,\(1\) 号点为根,边上有边权. 每个点有两个参数 \(p_i,q_i\) 如果你想从 \(i\) 号点到与其距离为 \(d\) 的 \(j\) 号点,那么你需花费 \( ...
- 【洛谷p3994】Highway 二分+斜率优化DP
题目大意:给你一颗$n$个点的有根树,相邻两个点之间有距离,我们可以从$x$乘车到$x$的祖先,费用为$dis\times P[x]+Q[x]$,问你除根以外每个点到根的最小花费. 数据范围:$n≤1 ...
- 洛谷P3994 Highway(树形DP+斜率优化+可持久化线段树/二分)
有点类似NOI2014购票 首先有方程$f(i)=min\{f(j)+(dep_i-dep_j)*p_i+q_i\}$ 这个显然是可以斜率优化的... $\frac {f(j)-f(k)}{dep_j ...
- 洛谷P2221 高速公路【线段树】
题目:https://www.luogu.org/problemnew/show/P2221 题意:有n个节点排成一条链,相邻节点之间有一条路. C u v val表示从u到v的路径上的每条边权值都加 ...
- 洛谷P2242 公路维修问题
To 洛谷.2242 公路维修问题 题目描述 由于长期没有得到维修,A国的高速公路上出现了N个坑.为了尽快填补好这N个坑,A国决定对M处地段采取交通管制.为了求解方便,假设A国的高速公路只有一条,而且 ...
- [洛谷P3761] [TJOI2017]城市
洛谷题目链接:[TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速 ...
- 洛谷 P5638 光骓者的荣耀
洛谷 P5638 [CSGRound2]光骓者的荣耀 洛谷传送门 题目背景 小 K 又在做白日梦了.他进入到他的幻想中,发现他打下了一片江山. 题目描述 小 K 打下的江山一共有nn个城市,城市ii和 ...
- 洛谷 P6383 -『MdOI R2』Resurrection(DP)
洛谷题面传送门 高速公路上正是补 blog 的时候,难道不是吗/doge,难不成逆在高速公路上写题/jy 首先形成的图显然是连通图并且有 \(n-1\) 条边.故形成的图是一棵树. 我们考虑什么样的树 ...
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
随机推荐
- flask_admin 笔记二 授权和权限
权限当然就是让有应该权限的用户能执行某些操作,把没有权限的用户限制在外面.Flask-admin提供了几种方法来处理: 1, Http basic Auth 最简单的身份验证形式是HTTP基本身份验证 ...
- web项目_学生证管理系统
项目简述: 基于java web实现学生卡管理系统,用户包括学生和学生处,分别实现以下功能. 学生: 1.对本人登录密码的修改 2.申请补办学生卡 3.查看学生卡补办状态 学生处: 1.对学生信息的管 ...
- Harbor私有镜像仓库无坑搭建
转载:https://k8s.abcdocker.com/kubernetes_harbor.html 一.介绍 Docker容器应用的开发和运行路不开可靠的镜像管理,虽然Docker官方也提供了公共 ...
- 高精度乘法--C++
高精度乘法--C++ 模仿竖式乘法,在第一步计算的时候将进位保留,第一步计算完再处理进位.(见代码注释) 若要处理正负情况,可在数据输入后加以判断,处理比较简单. 小数计算也可参照该方法,不过对齐方式 ...
- 初次接触Dynamics 365
最近项目上需要用到微软的Dynamics 365 这个产品,Bing上搜索了一下,看了很多大佬在博客上分享了使用Dynamics 365的经验,简单了解了Dynamics 365 是什么,也有很多大企 ...
- raft--分布式一致性协议
0. 写在前面的话 一直从事分布式对象存储工作,在分布式对象存储的运营,开发等工作中,数据一致性是至关重要的.因此想写一篇关于分布式一致性的文章.一来,可以和大家分享.二来,可以提高自己的文字提炼能力 ...
- 公钥与私钥的理解,以及https的应用原理
1.公钥与私钥原理1)鲍勃有两把钥匙,一把是公钥,另一把是私钥2)鲍勃把公钥送给他的朋友们----帕蒂.道格.苏珊----每人一把.3)苏珊要给鲍勃写一封保密的信.她写完后用鲍勃的公钥加密,就可以达到 ...
- Flask入门的第一个项目
前言: Flask简介:Flask是一个使用 Python 编写的轻量级 Web 应用框架,基于 WerkzeugWSGI工具箱和 Jinja2模板引擎. 想要学习flask,又非常迷茫,不知如何下手 ...
- #个人博客作业Week1----关于软件和软件工程的出现
1.软件工程这个词如何出现的? 数学与电脑科学先锋Margaret Hamilton在开发阿波罗11号软件的期间发明的,目的是将软件与硬件还有其他工程学类做出区别,为软件以及那些发明者争取应有的正统性 ...
- Day Five
站立式会议 站立式会议内容总结 442 今天:编写具体计划的界面 遇到的问题:相对布局.绝对布局理解不够深刻 明天:完成设定计划时间的功能 331 今天:添加书籍/计划按钮,添加书籍时有一个文件选择 ...