【BZOJ3672】【NOI2014】购票(线段树,斜率优化,动态规划)

题解

首先考虑\(dp\)的方程,设\(f[i]\)表示\(i\)的最优值

很明显的转移\(f[i]=min(f[j]+(dep[i]-dep[j])·p[i])+q[i]\)

其中满足\(dep[i]-dep[j]\le L[i]\)

然后就可以写出一个\(O(n^2)\)的做法啦

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline ll read()
{
RG ll x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
struct Line{int v,next,w;}e[MAX];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int n,type,fa[MAX];
ll S[MAX],P[MAX],Q[MAX],L[MAX];
ll dep[MAX],f[MAX];
void dfs(int u)
{
for(int i=h[u];i;i=e[i].next)
dep[e[i].v]=dep[u]+e[i].w,dfs(e[i].v);
}
double Slope(int a,int b){return (f[a]-f[b])*1.0/(dep[a]-dep[b]);}
namespace Brute
{
void DFS(int u)
{
if(u!=1)f[u]=1e18;
for(int i=fa[u];i&&dep[u]-dep[i]<=L[u];i=fa[i])
f[u]=min(f[u],f[i]+(dep[u]-dep[i])*P[u]+Q[u]);
for(int i=h[u];i;i=e[i].next)DFS(e[i].v);
}
void Solve()
{
DFS(1);
for(int i=2;i<=n;++i)printf("%lld\n",f[i]);
}
}
int main()
{
n=read();type=read();
for(int i=2;i<=n;++i)fa[i]=read(),S[i]=read(),P[i]=read(),Q[i]=read(),L[i]=read();
for(int i=2;i<=n;++i)Add(fa[i],i,S[i]);
dfs(1);
if(n<=2000){Brute::Solve();return 0;}
}

如果没有下面的那条限制,我们可以很容易的写出一个效率优化

设\(k<j<i\),\(j\)的转移优于\(k\),那么有

\(f[j]-dep[j]*p[i]<f[k]-dep[k]*p[i]\)

移项得到\(p[i]>\frac{f[j]-f[k]}{dep[j]-dep[k]}\)

很明显的斜率优化。

就这样我们就写出了一个\(t=0\)的\(20\)代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline ll read()
{
RG ll x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
struct Line{int v,next,w;}e[MAX];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int n,type,fa[MAX];
ll S[MAX],P[MAX],Q[MAX],L[MAX];
ll dep[MAX],f[MAX];
void dfs(int u)
{
for(int i=h[u];i;i=e[i].next)
dep[e[i].v]=dep[u]+e[i].w,dfs(e[i].v);
}
double Slope(int a,int b){return (f[a]-f[b])*1.0/(dep[a]-dep[b]);}
namespace Task0
{
int St[MAX],top;
int check(double K)
{
int l=2,r=top,ret=1;
while(l<=r)
{
int mid=(l+r)>>1;
if(Slope(St[mid],St[mid-1])>=K)r=mid-1;
else l=mid+1,ret=mid;
}
return St[ret];
}
void Solve()
{
St[top=1]=1;
for(int i=2;i<=n;++i)
{
int j=check(P[i]);
f[i]=f[j]+Q[i]+(dep[i]-dep[j])*P[i];
while(top>1&&Slope(i,St[top-1])<Slope(St[top],St[top-1]))--top;
St[++top]=i;
printf("%lld\n",f[i]);
}
}
}
int main()
{
n=read();type=read();
for(int i=2;i<=n;++i)fa[i]=read(),S[i]=read(),P[i]=read(),Q[i]=read(),L[i]=read();
for(int i=2;i<=n;++i)Add(fa[i],i,S[i]);
dfs(1);
if(type==0){Task0::Solve();return 0;}
}

当然,再把暴力给加上就有\(40\)分了。

(自己去拼接啊,要不然代码太多了)

剩下的部分分。

对于\(t=1\),我们发现是没有距离限制的

显然是对于每一条链维护一个凸包,所以只需要维护一个可持久化栈然后在上面二分就好了。

这个东西似乎可以用主席树维护,然后在主席树上面二分就行了。

(我就懒得写了)

对于\(t=2\),是一条链,但是有距离限制,

我们发现我们会从前面开始删去凸包上的一些点,这似乎非常不好做,

但是我们似乎可以用\(CDQ\)分治来做?按照能够用来更新的区间排序

每次\(CDQ\)的时候维护一段区间的凸包,然后更新一下答案就好了

(似乎可以这样做吧。。。)


现在终于可以来讲正解之一啦

现在的问题主要是如何动态维护凸包

其实我们把树直接树链剖分之后,对应的满足条件的是\(dfs\)序上的多段区间

所以我们用线段树暴力维护凸包,每个节点开一个\(vector\)

然后暴力维护这段区间的凸包

每个点最多会被\(log\)个线段树节点所包含,复杂度\(O(nlogn)\)

然后每次询问的时候在跳重链+线段树+二分

似乎是三个\(log\),但是我觉得只有两个\(log\)

似乎二分的\(log\)是加起来才有一个\(log\)吧。。。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
#define lson (now<<1)
#define rson (now<<1|1)
inline ll read()
{
RG ll x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
struct Line{int v,next;ll w;}e[MAX];
int h[MAX],cnt=1;
inline void Add(int u,int v,ll w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int n,type,fa[MAX];
ll S[MAX],P[MAX],Q[MAX],L[MAX];
ll dis[MAX],f[MAX];
int size[MAX],top[MAX],dfn[MAX],low[MAX],ln[MAX],tim,hson[MAX];
double Slope(int a,int b){return (f[a]-f[b])*1.0/(dis[a]-dis[b]);}
void dfs(int u)
{
size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;dis[v]=dis[u]+e[i].w;
dfs(v);size[u]+=size[v];
if(size[v]>size[hson[u]])hson[u]=v;
}
}
void dfs(int u,int tp)
{
top[u]=tp;dfn[u]=++tim;ln[tim]=u;
if(hson[u])dfs(hson[u],tp);
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=hson[u])dfs(e[i].v,e[i].v);
}
vector<int> t[MAX<<2];
void Modify(int now,int l,int r,int p)
{
int tp=t[now].size();
while(tp>1&&Slope(ln[p],t[now][tp-2])<Slope(t[now][tp-1],t[now][tp-2]))
--tp,t[now].pop_back();
t[now].push_back(ln[p]);
if(l==r)return;
int mid=(l+r)>>1;
if(p<=mid)Modify(lson,l,mid,p);
else Modify(rson,mid+1,r,p);
}
ll Calc(vector<int> t,int u)
{
int l=1,r=t.size()-1,ret=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(Slope(t[mid],t[mid-1])<1.0*P[u])l=mid+1,ret=mid;
else r=mid-1;
}
int v=t[ret];
return f[v]+(dis[u]-dis[v])*P[u]+Q[u];
}
ll Query(int now,int l,int r,int L,int R,int u)
{
if(L<=l&&r<=R)return Calc(t[now],u);
int mid=(l+r)>>1;ll ret=1e18;
if(L<=mid)ret=min(ret,Query(lson,l,mid,L,R,u));
if(R>mid)ret=min(ret,Query(rson,mid+1,r,L,R,u));
return ret;
}
int Top=0;
void Jump(int u,int anc)
{
f[u]=5e18;int U=u;u=fa[u];
while(top[u]^top[anc])
{
f[U]=min(f[U],Query(1,1,n,dfn[top[u]],dfn[u],U));
u=fa[top[u]];
}
f[U]=min(f[U],Query(1,1,n,dfn[anc],dfn[u],U));
}
void DFS(int u)
{
S[++Top]=u;
if(u!=1)
{
int l=1,r=Top-1,v=u;
while(l<=r)
{
int mid=(l+r)>>1;
if(dis[u]-dis[S[mid]]<=L[u])v=S[mid],r=mid-1;
else l=mid+1;
}
Jump(u,v);
}
Modify(1,1,n,dfn[u]);
for(int i=h[u];i;i=e[i].next)DFS(e[i].v);
--Top;
}
int main()
{
n=read();type=read();
for(int i=2;i<=n;++i)fa[i]=read(),S[i]=read(),P[i]=read(),Q[i]=read(),L[i]=read();
for(int i=2;i<=n;++i)Add(fa[i],i,S[i]);
dfs(1);dfs(1,1);memset(S,0,sizeof(S));DFS(1);
for(int i=2;i<=n;++i)printf("%lld\n",f[i]);
return 0;
}

【BZOJ3672】【NOI2014】购票(线段树,斜率优化,动态规划)的更多相关文章

  1. BZOJ_3672_ [Noi2014]购票_CDQ分治+斜率优化

    BZOJ_3672_ [Noi2014]购票_CDQ分治+斜率优化 Description  今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参 ...

  2. UOJ#7. 【NOI2014】购票 | 线段树 凸包优化DP

    题目链接 UOJ #7 题解 首先这一定是DP!可以写出: \[f[i] = \min_{ancestor\ j} \{f[j] + (d[j] - d[i]) * p[i] + q[i]\}\] 其 ...

  3. [NOI2014]购票 「树上斜率优化」

    首先易得方程,且经过变换有 $$\begin{aligned} f_i &= \min\limits_{dist_i - lim_i \le dist_j} \{f_j + (dist_i - ...

  4. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  5. BZOJ3672: [Noi2014]购票【CDQ分治】【点分治】【斜率优化DP】

    Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的 ...

  6. BZOJ3672 [Noi2014]购票 【点分治 + 斜率优化】

    题目链接 BZOJ3672 题解 如果暂时不管\(l[i]\)的限制,并假使这是一条链 设\(f[i]\)表示\(i\)节点的最优答案,我们容易得到\(dp\)方程 \[f[i] = min\{f[j ...

  7. bzoj3672: [Noi2014]购票(树形DP+斜率优化+可持久化凸包)

    这题的加强版,多了一个$l_i$的限制,少了一个$p_i$的单调性,难了好多... 首先有方程$f(i)=min\{f(j)+(dep_i-dep_j)*p_i+q_i\}$ $\frac {f(j) ...

  8. [BZOJ3672][Noi2014]购票 斜率优化+点分治+cdq分治

    3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1749  Solved: 885[Submit][Status][ ...

  9. UOJ#7. 【NOI2014】购票 点分治 斜率优化 凸包 二分

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ7.html 题解 这题是Unknown的弱化版. 如果这个问题出在序列上,那么显然可以CDQ分治 + 斜率 ...

  10. bzoj千题计划251:bzoj3672: [Noi2014]购票

    http://www.lydsy.com/JudgeOnline/problem.php?id=3672 法一:线段树维护可持久化单调队列维护凸包 斜率优化DP 设dp[i] 表示i号点到根节点的最少 ...

随机推荐

  1. 【转】linux下,如何把整个文件夹上传到服务器(另一台linux)

    原文转自:https://zhidao.baidu.com/question/1046040541327493019.html 1.Linux下目录复制:本机->远程服务器 scp  -r /h ...

  2. macOS中启动Tomcat提示Cannot find ./catalina.sh

    首先查看Tomcat目录下是否存在catalina.sh,如果文件不存在,文件丢失,最好的方式是重装Tomcat Tomcat官网:http://tomcat.apache.org/ 如果文件存在,那 ...

  3. Docker--从安装到搭建环境

    docker 1. ubuntu下安装docker 安装docker有两种方法: 一种是用官方的bash脚本一键安装. 直接一条命令就解决了: $ curl -sSL https://get.dock ...

  4. Selenium2+python自动化-环境搭建

    一.selenium简介 Selenium 是用于测试 Web 应用程序用户界面 (UI) 的常用框架.它是一款用于运行端到端功能测试的超强工具.您可以使用多个编程语言编写测试,并且 Selenium ...

  5. kubeadm构建k8s之Prometheus-operated监控(0.18.1)

    介绍: 大家好,k8s的搭建有许多方式,也有许多快速部署的,为了简化部署的复杂度,官方也提供了开源的kubeadm快速部署,最新1.10.x版本已经可以实现部署集群, 如果你对k8s的原理已经非常了解 ...

  6. We are writing to let you know we have removed your selling privileges

     Hello, We are writing to let you know we have removed your selling privileges, canceled your listin ...

  7. JS中自定义事件的使用与触发

    1. 事件的创建 JS中,最简单的创建事件方法,是使用Event构造器: var myEvent = new Event('event_name'); 但是为了能够传递数据,就需要使用 CustomE ...

  8. [Linux] Migrate plugins and setting for vim

    Hot to migrate plugins and setting for vim from one computer to another ? Just copy  ~/.vim and ~/.v ...

  9. 原生js和jquey获取窗口宽高,滚动条,鼠标位置总结

    JQuery获取浏览器窗口的可视区域高度和宽度,滚动条高度   alert($(window).height()); //浏览器时下窗口可视区域高度 alert($(document).height( ...

  10. 电梯V2.0

    电梯V2.0 GitHub仓库地址 Problem 一栋10层的大楼(楼层编号1-10),设3台无限载重的电梯,初始时电梯停在1层.其中:1号电梯只能停留在奇数层,2号电梯可以各层都停留,3号电梯只停 ...