题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3672

题意:给出一棵有根树(1为根),边有长度。每个点u有三个属性(len[u],p[u],q[u]),每次u可以转移到u的某个祖先节点v(v满足dist(u,v)<=len[u]),代价为p[u]*dist(u,v)+q[u]。求每个点都转移到1的代价。

思路:首先设f[u]表示u转移到1的最小代价,那么我们可以得到一个DP方程: f[u]=min(f[v]+p[u]*(s[u]-s[v])+q[u]) (v为u的祖先节点,s[u]表示u到根节点1的距离,s[u]-s[v]<=len[u])。

将上面的式子变形:f[u]=(-s[v]*p[u]+f[v])+(s[u]*p[u]+q[u])。其中s[u]*p[u]+q[u]对于u为定值那么我们只要求-s[v]*p[u]+f[v]的最小值即可。

我们按照深度由小大到的顺序依次计算,那么计算u时,它的所有祖先节点都已经计算过,他们的f[v]和s[v]值都已经知道。那么,我们断然是不能一个一个枚举u的所有祖先的.因此如何维护呢?设k=-s[v],b=f[v],x=p[u],y=kx+b,我们现在就是求y的最小值,k<0,斜率k,y轴截距b。我们发现,只要维护[1,fa[u]]这些点组成的上凸壳即可。

我们设现在已经维护了一个上凸壳,现在加入一个点(-s[t],f[t]),我们发现,由于题目说边的距离严格大于0,那么这个s[t]是严格递增的,也就是斜率越来越趋近于负无穷,因此,我们首先将其加入到已经维护的凸壳的最后即可(当x很大时一定是这个新加入的能够使得答案最小)。此时,前面可能有一些不再是最优值,我们只要依次从后向前判断,不是最优值就删掉,因此用一个vector维护即可(不用splay、set什么的了)。

因此,我们得到算法:

(1)首先,树链剖分,用线段树维护每个链,线段树每个节点用一个vector维护这个区间的点组成的上凸壳

(2)按照深度由小到大依次计算。由于这里有一个距离限制,那么对于u,最后我们需要查询的是一段区间[v,fa[u]],v是满足s[u]-s[v]<=len[u]的深度最小的u的祖先。

(3)计算完节点u后将其插入到线段树中包含该点的所有区间。

const i64 inf=(1LL<<62);
const int mod=1000000007;
const int N=200005; vector<int > g[N];
int n,t;
int fa[N];
i64 len[N],p[N],q[N],dis[N]; int sonNum[N];
int dep[N]; i64 s[N]; void DFS(int u)
{
sonNum[u]=1;
int i;
for(i=0;i<SZ(g[u]);i++)
{
int v=g[u][i];
dep[v]=dep[u]+1;
s[v]=s[u]+dis[v];
DFS(v);
sonNum[u]+=sonNum[v];
}
} int id,belong[N],pos[N],mp[N]; void dfs(int u,int root)
{
id++;
belong[u]=root;
pos[u]=id;
mp[id]=u;
int i,k=n+1;
for(i=0;i<SZ(g[u]);i++) if(sonNum[k]<sonNum[g[u][i]]) k=g[u][i];
if(k==n+1) return;
dfs(k,root);
for(i=0;i<SZ(g[u]);i++)
{
if(g[u][i]!=k) dfs(g[u][i],g[u][i]);
}
} struct node
{
int L,R;
vector<pair<i64,i64> > root;
}; node A[N<<2]; void build(int t,int L,int R)
{
A[t].L=L;
A[t].R=R;
A[t].root.clear();
if(L==R)
{
return;
}
int M=(L+R)>>1;
build(t<<1,L,M);
build(t<<1|1,M+1,R);
} #define pdd pair<i64,i64> i64 f[N]; double cross(pdd a,pdd b)
{
return 1.0*(a.second-b.second)/(b.first-a.first);
} int sgn(double x)
{
if(x>1e-20) return 1;
if(x<-1e-20) return -1;
return 0;
} void add(vector<pair<i64,i64> > &x,i64 s,i64 f)
{
pdd p3=MP(s,f);
while(SZ(x)>=2)
{
pdd p2=x[SZ(x)-1];
pdd p1=x[SZ(x)-2];
double x1=cross(p2,p3);
double x2=cross(p1,p3);
if(sgn(x1-x2)==1) break;
x.pop_back();
}
if(SZ(x)==1&&f<=x[0].second) x.pop_back();
x.pb(p3);
} void add(int t,int pos,i64 s,i64 f)
{
add(A[t].root,s,f);
if(A[t].L==A[t].R) return;
int M=(A[t].L+A[t].R)>>1;
if(pos<=M) add(t<<1,pos,s,f);
else add(t<<1|1,pos,s,f);
} i64 curX; i64 get(vector<pdd> x)
{
int L=0,R=SZ(x)-1;
while(R-L>=4)
{
int M=(R+L)>>1;
double xx=cross(x[M-1],x[M]);
if(sgn(xx-curX)>=0) R=M;
else L=M;
}
i64 ans=inf;
int i;
for(i=L;i<=R;i++)
{
pdd p=x[i];
i64 tmp=curX*p.first+p.second;
if(tmp<ans) ans=tmp;
}
return ans;
} i64 cal(int t,int L,int R,i64 len)
{
if(A[t].L==L&&A[t].R==R)
{
int u1=mp[L];
int u2=mp[R];
if(s[u2]-s[u1]<=len) return get(A[t].root); int M=(A[t].L+A[t].R)>>1;
i64 ans=cal(t<<1|1,M+1,R,len);
u1=mp[A[t<<1].R];
len-=(s[u2]-s[u1]);
if(len<0) return ans;
i64 tmp=cal(t<<1,L,M,len);
if(tmp<ans) ans=tmp;
return ans;
}
else
{
int M=(A[t].L+A[t].R)>>1;
if(R<=M) return cal(t<<1,L,R,len);
if(L>M) return cal(t<<1|1,L,R,len);
i64 ans=cal(t<<1|1,M+1,R,len);
int u1=mp[A[t<<1].R];
int u2=mp[R];
len-=(s[u2]-s[u1]);
if(len<0) return ans; i64 tmp=cal(t<<1,L,M,len);
if(tmp<ans) ans=tmp;
return ans;
}
} i64 cal(int u)
{
curX=p[u];
i64 L=len[u];
i64 ans=inf;
while(L>=0)
{
i64 tmp=cal(1,pos[belong[u]],pos[u],L);
if(tmp<ans) ans=tmp;
if(fa[belong[u]]==0) break;
L-=(s[u]-s[fa[belong[u]]]);
u=fa[belong[u]];
}
return ans;
} int main()
{
scanf("%d%d",&n,&t);
int i;
for(i=2;i<=n;i++)
{
scanf("%d%lld%lld%lld%lld",&fa[i],&dis[i],&p[i],&q[i],&len[i]);
g[fa[i]].pb(i);
}
DFS(1);
dfs(1,1);
build(1,1,n);
add(1,pos[1],0,0);
for(i=2;i<=n;i++)
{
int u=mp[i];
f[u]=cal(u)+p[u]*s[u]+q[u];
add(1,pos[u],-s[u],f[u]);
}
for(i=2;i<=n;i++) printf("%lld\n",f[i]);
}

BZOJ 3672 [Noi2014]购票 (熟练剖分+凸壳维护)的更多相关文章

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

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

  2. bzoj 3672: [Noi2014]购票 树链剖分+维护凸包

    3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 480  Solved: 212[Submit][Status][D ...

  3. BZOJ 3672: [Noi2014]购票( 树链剖分 + 线段树 + 凸包 )

    s弄成前缀和(到根), dp(i) = min(dp(j) + (s(i)-s(j))*p(i)+q(i)). 链的情况大家都会做...就是用栈维护个下凸包, 插入时暴力弹栈, 查询时就在凸包上二分/ ...

  4. ●BZOJ 3672 [Noi2014]购票

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3672 题解: 斜率优化DP,点分治(树上CDQ分治...) 这里有一个没有距离限制的简单版: ...

  5. BZOJ 3672 [NOI2014]购票 (凸优化+树剖/树分治)

    题目大意: 略 题面传送门 怎么看也是一道$duliu$题= = 先推式子,设$dp[x]$表示到达$x$点到达1节点的最小花费 设$y$是$x$的一个祖先,则$dp[x]=min(dp[y]+(di ...

  6. bzoj 3672: [Noi2014]购票

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

  7. BZOJ 3672: [Noi2014]购票 树上CDQ分治

    做这道题真的是涨姿势了,一般的CDQ分治都是在序列上进行的,这次是把CDQ分治放树上跑了~ 考虑一半的 CDQ 分治怎么进行: 递归处理左区间,处理左区间对右区间的影响,然后再递归处理右区间. 所以, ...

  8. 【BZOJ 3672】 3672: [Noi2014]购票 (CDQ分治+点分治+斜率优化)**

    3672: [Noi2014]购票 Description  今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会.        全国 ...

  9. BZOJ.1007.[HNOI2008]水平可见直线(凸壳 单调栈)

    题目链接 可以看出我们是要维护一个下凸壳. 先对斜率从小到大排序.斜率最大.最小的直线是一定会保留的,因为这是凸壳最边上的两段. 维护一个单调栈,栈中为当前可见直线(按照斜率排序). 当加入一条直线l ...

随机推荐

  1. -XX:+PrintGCDetails 打印GC详细信息

    -XX:+PrintGCDetails –打印GC详细信息     n-XX:+PrintGCDetails的输出 –Heap – def new generation   total 13824K, ...

  2. EBS R12版 GL追溯到各个模块

    应收.应付.收款.付款等单据都可以生成ERP的日记帐,那么这些模块的关系是如何关联的呢,我们将会解决这个问题. 各个模块与总帐模块的关系,主要是通过子分类帐来进行关联的. 下面的SQL就是总帐与子分类 ...

  3. zw·准专利·高保真二值图细部切分算法

    zw·准专利·高保真二值图细部切分算法     高保真二值图细部切分算法,是中国字体协会项目的衍生作品.     说准专利算法,是因为对于图像算法的标准不了解,虽然报过专利,但不是这方面的,需要咨询专 ...

  4. Limit the query running time with Resource limit facility (RLF)

    If you need to limit the query(package,plan) running time, but the JCL/JOB TIME parameters doesn't w ...

  5. Sql Server 2012 的新分页方法分析(offset and fetch) - 转载

    最近在分析 Sql Server 2012 中 offset and fetch 的新特性,发现 offset and fetch 无论语法的简洁还是功能的强大,都是相当相当不错的 其中 offset ...

  6. vsftpd基本配置(原)

    前提:iptables关闭或放行,selinux关闭或放行. 目标:创建虚拟ftpuser到指定的www.ftpuser.com目录,且只能在本目录下. 创建帐号 useradd -d /var/ww ...

  7. vsftp黑白名单设置及问题

    问题一:ftpusers和user_list两个文件各自的用途是什么?有何关系? 首先请明确一点:ftpusers不受任何配制项的影响,它总是有效,它是一个黑名单!该文件存放的是一个禁止访问FTP的用 ...

  8. Android NDK 开发(三)--常见错误锦集合Log的使用【转】

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/41826511  Android NDK开发经常因某些因素会出现一些意想不到的错误, ...

  9. scala匿名函数

    package com.ming.test import scala.math._ object AnonymousFunc { def valueAtOneQuarter(f:(Double)=&g ...

  10. git 用法

    git init #创建仓库git add _filename #添加文件到缓冲区git commit -m "msg" #提交更新,从缓冲区提交到版本库git status #查 ...