[Noi2014]购票 BZOJ3672 点分治+斜率优化+CDQ分治
Description
Input
第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到)。输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市。其中第 v 行包含 5 个非负整数 f_v,s_v,p_v,q_v,l_v,分别表示城市 v 的父亲城市,它到父亲城市道路的长度,票价的两个参数和距离限制。请注意:输入不包含编号为 1 的SZ市,第 2 行到第 n 行分别描述的是城市 2 到城市 n。
Output
输出包含 n-1 行,每行包含一个整数。其中第 v 行表示从城市 v+1 出发,到达SZ市最少的购票费用。同样请注意:输出不包含编号为 1 的SZ市。
Sample Input
1 2 20 0 3
1 5 10 100 5
2 4 10 10 10
2 9 1 100 10
3 5 20 100 10
4 4 20 0 10
Sample Output
150
70
149
300
150
对于所有测试数据,保证 0≤pv≤106,0≤qv≤1012,1≤fv<v;保证 0<sv≤lv≤2×1011,且任意城市到SZ市的总路程长度不超过 2×1011。
输入的 t 表示数据类型,0≤t<4,其中:
当 t=0 或 2 时,对输入的所有城市 v,都有 fv=v-1,即所有城市构成一个以SZ市为终点的链;
当 t=0 或 1 时,对输入的所有城市 v,都有 lv=2×1011,即没有移动的距离限制,每个城市都能到达它的所有祖先;
当 t=3 时,数据没有特殊性质。
n=2×10^5

分析:
先考虑一下暴力...其实前3个点的分还是很好拿的...稍微搞一搞就出来了,n^2DP比较显然...
后面的呢,应该拿50分没有什么大问题的说...一条链的斜率优化实在不能再好写一点了...
附上30分的暴力:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
#include <cstdlib>
using namespace std;
#define N 200005
#define ll long long
struct node{int to,next;}e[N<<1];
int fa[N],n,head[N],cnt;
ll f[N],dep[N],l[N],p[N],q[N],a[N];
void add(int x,int y){e[cnt]=(node){y,head[x]};head[x]=cnt++;}
void dfs(int x,int from)
{
fa[x]=from;dep[x]=dep[from]+a[x];
for(int t=fa[x];t&&dep[x]-dep[t]<=l[x];t=fa[t])f[x]=min(f[t]+(dep[x]-dep[t])*p[x]+q[x],f[x]);
for(int i=head[x];i!=-1;i=e[i].next)if(e[i].to!=from)dfs(e[i].to,x);
}
int main()
{
scanf("%d%*d",&n);memset(head,-1,sizeof(head));memset(f,0x3f,sizeof(f));
for(int i=2;i<=n;i++)
{
scanf("%d%lld%lld%lld%lld",&fa[i],&a[i],&p[i],&q[i],&l[i]);
add(fa[i],i);add(i,fa[i]);
}f[1]=0;dfs(1,0);
for(int i=2;i<=n;i++)printf("%lld\n",f[i]);return 0;
}
目测我似乎能跑过4个点...反正就是这个样子的暴力...
f[x]=max{f[j]+(dep[x]-dep[j])*p[x]+q[x]};(j是x的祖先,满足dep[x]-dep[j]<=l[x])
显然...发现dep单调,p[x]不单调,但这不是问题用不着写什么奇怪的东西,每次二分导函数即可...
但是,这个题出在树上就让人很崩溃了...显然,我们考虑有两种斜率优化的方法可以选择。
(1)动态维护凸包+二分查找 → 如果你会可持久化凸包的话可以考虑考虑,至于树剖+动态凸包能不能过...反正时间复杂度有问题...nlog^3n实在是不忍直视...
(2)CDQ分治(前置技能:货币兑换)每次考虑如何维护凸包用来更新节点,那么考虑选择一个链更新一个点的子树,那么这样的话,只需要维护一个凸包就可以了。这种情况下,满足直接二分转移一下就可以了。那么如何选择这个点,显然,我们可以选择树的重心,因为这样的话,满足子树的最大一个小于n/2,转移次数<=logn(点分治),每次遍历满,时间复杂度O(nlog^2n)。
可以考虑,如果一个点,满足所有的父亲节点都被确定,那么这个点就可以被确定,并且可以用这条链更新这个点的子树,那么我们每次找到重心之后,先找到它的父节点那部分作为CDQ分治的左部分,用左部分更新重心的子树并将其更新,之后再递归处理其他的子部分。而每次可以发现,我们dep是单调递增的,维护一个凸包,之后按照子树中节点能够更新的最深的位置进行排名,每次进行二分导函数,找到最优解(同任务安排),其实也就是把树分治当做CDQ分治来做。
附上代码:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <bitset>
using namespace std;
#define N 200005
#define ll long long
#define K(x) (-dep[x])
#define B(x) (f[x])
#define Y(x,y) (K(x)*p[y]+B(x))
struct egde{int to,next;}e[N<<1];
int head[N],cnt,fa[N],siz[N],sn,rot,mx[N],vis[N],q[N],lx[N],ln,n,rx[N],rn,tail;
ll dep[N],p[N],l[N],f[N],b[N];
void add(int x,int y){e[cnt]=(egde){y,head[x]};head[x]=cnt++;}
void get_root(int x,int from)
{
siz[x]=1,mx[x]=0;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from&&!vis[to1])
{
get_root(to1,x);
siz[x]+=siz[to1];
mx[x]=max(siz[to1],mx[x]);
}
}
mx[x]=max(mx[x],sn-siz[x]);
if(mx[x]<mx[rot])rot=x;
}
bool cmp(int i,int j,int k)
{
long double t1=(long double)(K(k)-K(i))*(B(k)-B(j));
long double t2=(long double)(K(k)-K(j))*(B(k)-B(i));
return t1>=t2-1e-10;
}
bool cmp1(const int &a,const int &b){return dep[a]-l[a]>dep[b]-l[b];}
void get_rx(int x,int from)
{
if(from!=0)rx[++rn]=x;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from&&!vis[to1])get_rx(to1,x);
}
}
void Update(int x)
{
if(!tail)return ;int l=1,r=tail;
while(l<r)
{
int m=(l+r)>>1;
if(Y(q[m],x)>Y(q[m+1],x))l=m+1;
else r=m;
}
f[x]=min(f[x],Y(q[l],x)+b[x]);
}
void dfs(int x)
{
int rt;
sn=siz[x],rot=0,get_root(x,0),rt=rot,vis[rt]=1;
// printf("%d %d\n",x,rt);
if(x!=rt)siz[x]-=siz[rt],dfs(x);
tail=ln=rn=0;lx[++ln]=rt;
for(int i=rt;i!=x;i=fa[i])
{
if(dep[rt]-l[rt]<=dep[fa[i]])f[rt]=min(f[rt],Y(fa[i],rt)+b[rt]);
lx[++ln]=fa[i];
}
get_rx(rt,0);sort(rx+1,rx+rn+1,cmp1);
// if(x==1)for(int i=1;i<=rn;i++)printf("%d %d\n",rt,rx[i]);
int j=1;
for(int i=1;i<=ln&&j<=rn;i++)
{
while(j<=rn&&dep[lx[i]]<dep[rx[j]]-l[rx[j]])Update(rx[j++]);
while(tail>1&&cmp(q[tail],q[tail-1],lx[i]))tail--;
q[++tail]=lx[i];
}while(j<=rn)Update(rx[j++]);
for(int i=head[rt];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(!vis[to1])dfs(to1);
}
}
int main()
{
scanf("%d%*d",&n);memset(f,0x3f,sizeof(f));memset(head,-1,sizeof(head));
for(int i=2;i<=n;i++)
{
scanf("%d%lld%lld%lld%lld",&fa[i],&dep[i],&p[i],&b[i],&l[i]);
dep[i]+=dep[fa[i]];b[i]+=dep[i]*p[i];add(fa[i],i);add(i,fa[i]);
// printf("%d\n",b[i]);
}f[1]=0;mx[0]=1<<30,siz[1]=n;dfs(1);
for(int i=2;i<=n;i++)printf("%lld\n",f[i]);return 0;
}
[Noi2014]购票 BZOJ3672 点分治+斜率优化+CDQ分治的更多相关文章
- BZOJ_3963_[WF2011]MachineWorks_斜率优化+CDQ分治
BZOJ_3963_[WF2011]MachineWorks_斜率优化+CDQ分治 Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM) ...
- 【BZOJ-1492】货币兑换Cash DP + 斜率优化 + CDQ分治
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 3396 Solved: 1434[Submit][Sta ...
- 洛谷.4655.[CEOI2017]Building Bridges(DP 斜率优化 CDQ分治)
LOJ 洛谷 \(f_i=s_{i-1}+h_i^2+\min\{f_j-s_j+h_j^2-2h_i2h_j\}\),显然可以斜率优化. \(f_i-s_{i-1}-h_i^2+2h_ih_j=f_ ...
- BZOJ3963 WF2011MachineWorks(动态规划+斜率优化+cdq分治)
按卖出时间排序后,设f[i]为买下第i台机器后的当前最大收益,则显然有f[i]=max{f[j]+gj*(di-dj-1)+rj-pi},且若此值<0,应设为-inf以表示无法购买第i台机器. ...
- bzoj1492/luogu4027 货币兑换 (斜率优化+cdq分治)
设f[i]是第i天能获得的最大钱数,那么 f[i]=max{在第j天用f[j]的钱买,然后在第i天卖得到的钱,f[i-1]} 然后解一解方程什么的,设$x[j]=\frac{F[j]}{A[j]*Ra ...
- [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5838 Solved: 2345[Submit][Sta ...
- 【BZOJ2726】[SDOI2012]任务安排 斜率优化+cdq分治
[BZOJ2726][SDOI2012]任务安排 Description 机器上有N个需要处理的任务,它们构成了一个序列.这些任务被标号为1到N,因此序列的排列为1,2,3...N.这N个任务被分成若 ...
- 【BZOJ1492】[NOI2007]货币兑换Cash 斜率优化+cdq分治
[BZOJ10492][NOI2007]货币兑换Cash Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券).每 ...
- BZOJ2726 [SDOI2012]任务安排 【斜率优化 + cdq分治】
题目 机器上有N个需要处理的任务,它们构成了一个序列.这些任务被标号为1到N,因此序列的排列为1,2,3...N.这N个任务被分成若干批,每批包含相邻的若干任务.从时刻0开始,这些任务被分批加工,第i ...
随机推荐
- JS 数组对象根据下标拆分成新的数组
真为难啊! var arr = [ {guigeArr:['蓝色','XL','3','S']}, {guigeArr:['蓝色','L','6','S']}, {guigeArr:['蓝色','L' ...
- Tracing 在PeopleSoft 程序中怎么开启
本文介绍一些常用的跟踪方法在Applications,Application Engine,PeopleSoft,Integration Broker,Cobol中. 1.Application En ...
- ActiveReports 报表控件V12新特性 -- 新增矩表的RepeatToFill属性
ActiveReports是一款专注于 .NET 平台的报表控件,全面满足 HTML5 / WinForms / ASP.NET / ASP.NET MVC / WPF 等平台下报表设计和开发工作需求 ...
- WampServer下修改和重置MySQL密码
Wampserver PHP环境中mysql数据库登录密码的修改和重置,mysql命令. 工具/原料 电脑Windows系统 WampServer 方法/步骤1 启动WampSer ...
- Sql server 索引详解
参考资料:老K写的,http://www.cnblogs.com/AK2012/archive/2013/01/04/2844283.html SQL索引在数据库优化中占有一个非常大的比例, 一个好的 ...
- [WPF 容易忽视的细节] —— Exception in WPF's Converter
前言: 在WPF中,Converter是我们经常要用到的一个工具,因为XAML上绑定的数据不一定是我们需要的数据. 问题: 在Converter中抛出一个异常导致程序崩溃,而且是在对未捕获异常进行集中 ...
- 【Python】多线程
import threading import time class myThread (threading.Thread): #继承父类threading.Thread def __init__(s ...
- GIT回推本地commit近期版本
一次意外吧,本地add并且commit之后拉了别人错误的代码下来,后来本地又需要进行编写测试,无奈只能回推到自己刚刚commit过的代码,但是git命令除了拉去提交等其他的不是很熟悉,在度娘之后也遇到 ...
- oracle 11.2.0.1 rman异机恢复 11.2.0.3(windows X64)
问题原因: 误操作,需要时间点恢复. 备份情况:rman 备份,每天一次全备份,并且附带备份当天所有产生的archivelog,无expdp备份 恢复目标: 恢复到9号晚上21点数据 源系统:WIND ...
- 网工最实用最常用的网络命令之一——Ping 命令详解(一)
Ping是Windows.Unix和Linux系统下的一个命令.ping也属于一个通信协议,是TCP/IP协议的一部分.利用“ping”命令可以检查网络是否连通,可以很好地帮助我们分析和判定网络故障. ...