Portal -->bzoj3672

Solution

  天知道我是怎么调完的qwq调到天昏地暗系列。。

​  

  不管这么多,先尝试列一个最简单的状态转移方程

  用\(f[i]\)表示\(i\)点到\(1\)号点要花费的最少资金,\(dis[i]\)表示\(i\)到\(1\)号点的距离,那么有:

\[f[i]=min(f[j]+p[i]*(dis[i]-dis[j])+q[i])
\]

  其中\(j\)是\(i\)的祖先且\(dis[i]-dis[j]<=l[i]\)

​  然后直接转移什么的肯定是不现实的啦。。所以我们可以先看看dp本身可以用什么优化

​  首先这个转移式子,我们换一个方式来写一下:

\[f[j]=p[i]*dis[j]+(f[i]-p[i]*dis[i]+q[i])
\]

​  当考虑\(i\)的时候,\(-p[i]*dis[i]+q[i]\)可以看成一个常数,\(p[i]\)也可以看成一个常数,那么上面这个式子可以看成\(y=kx+b\)的形式,也就是说,是一条斜率为\(p[i]\),截距为\((f[i]-p[i]*dis[i]+q[i])\)的直线

  然后我们可以考虑斜率优化啦,因为斜率不是单调的所以还是要老老实实在凸包上二分,然后这里我们要\(f[i]\)最小所以是维护下凸壳

  

  然而

​  这题的dp在树上。。而且还有两个限制条件(\(lca(i,j)=j\)且\(dis[i]-dis[j]<=l[i]\))

​  关于\(lca(i,j)=j\)这个条件,自己一开始有一个比较初步的想法是每次用这个点去更新其子树还想着写线段树然后再每个区间维护凸包什么之类的。。然而实际上这里有一种比较高级的姿势:

​  树上cdq分治(emm大神的博客里面是这么叫的)

​   

​  这里的cdq分治的话,大致的过程是(其实写起来跟点分差不多的。。看到网上也有大神说这个就是点分。。):

1、找一个点\(x\)将树分为上下两个部分(为了保证复杂度这里的\(x\)显然应该要找重心)

2、先递归处理这个点\(x\)以上的(也就是包含当前根节点的)那一部分(相当于一般cdq的处理左边区间)

3、用上一步中处理出来的\(x\)的祖先的信息来更新\(x\)的子树(相当于统计左边对右边贡献)

4、递归处理\(x\)的子树(处理右边)

​  

​  这样我们就解决了\(lca(i,j)=j\)的问题,接下来是第二个限制\(dis[i]-dis[j]<=l[i]\)

​  我们稍微处理一下这个不等式,变成:\(dis[i]-l[i]<=dis[j]\)

​  现在我们考虑用不同的\(j\)去更新\(i\)(就是步骤3中用祖先更新子树)

  因为\(dis\)是单调的,我们可以在更新之前先排个序,子树内的点按照\(dis[i]-l[i]\)从大到小排,\(dis[j]\)也是从大到小(其实两个都反过来也是可以的,只是因为我在程序里面写的时候求祖先是暴力跳的,所以祖先的\(dis\)求出来就是从大到小的顺序,所以就这么写了)

​  我们按顺序枚举祖先,每次在将当前枚举到的祖先\(j\)加到凸包里面去之前,先判断一下当前的\(dis[i]-l[i]\)是否\(<=dis[j]\),如果不是的话,那么就在还没有加入祖先\(j\)的凸包中二分来更新\(f[i]\),然后再将祖先\(j\)加入凸包;否则直接将\(j\)加入凸包

​  这里因为\(dis\)(也就是凸包上点的\(x\)坐标)是单调的所以可以直接单调栈维护

​  这样就能保证我们在凸包中二分出来的答案一定是满足\(dis[i]-dis[j]<=l[i]\)的

​  然后就非常愉悦地做完了,总的复杂度是\(O(nlog^2n)\)

​  

  一些可能要注意的细节&自己跳进去的坑:

1、本来凸包是写的叉积的。。但是后来看了一下数据范围有点担心\(x*y\)一下直接爆longlong了。。所以就改成斜率了。。还是要注意一下可能会出现两个\(x\)相等的情况所以要判一下避免除以\(0\)(之前那个坑爬得太艰难再也不相信除法了qwq)

2、注意是下凸壳下凸壳下凸壳!(调着调着把自己调懵了系列。。)

3、建凸包的时候注意祖先是按照\(dis\)从大到小来排的。。也就是横坐标是递减的。。

4、看清数据范围。。各种long long

​  

​  代码大概长这个样子(思路好像不算特别绕但是。。调起来就很。。了。。)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
const int MAXN=2*(1e5)+10;
const ll inf=1LL<<60;
struct xxx{
int y,nxt;
}a[MAXN*2];
int h[MAXN],sz[MAXN],vis[MAXN],mx[MAXN],pre[MAXN];
int rec[MAXN],st[MAXN],rec_pre[MAXN];
ll p[MAXN],q[MAXN],l[MAXN],dis[MAXN];
ll f[MAXN];
int n,m,tot,rt,rt_mx,All,T;
void add(int x,int y);
void get_sz(int fa,int x);
void get_rt(int All,int fa,int x);
void dfs(int fa,int x);
void solve(int x,int All);
bool cmp(int x,int y){return dis[x]-l[x]>dis[y]-l[y];}
void update(int x,int top);
ll X(int i){return dis[i];}
ll Y(int i){return f[i];}
double get_k(int i,int j){if (X(i)==X(j)) return inf; return (1.0*(Y(i)-Y(j)))/(1.0*(X(i)-X(j)));} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d%d\n",&n,&T);
memset(h,-1,sizeof(h));
tot=0;
for (int i=2;i<=n;++i){
scanf("%d%lld%lld%lld%lld\n",pre+i,dis+i,p+i,q+i,l+i);
dis[i]+=dis[pre[i]];
add(pre[i],i);
}
for (int i=1;i<=n;++i) f[i]=inf;
f[1]=0;
solve(1,n);
for (int i=2;i<=n;++i) printf("%lld\n",f[i]);
} void add(int x,int y){
a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;
} void get_sz(int fa,int x){
int u;
sz[x]=1; mx[x]=0;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
get_sz(x,u);
sz[x]+=sz[u];
mx[x]=max(mx[x],sz[u]);
}
} void get_rt(int All,int fa,int x){
mx[x]=max(mx[x],All-sz[x]);
if (mx[x]<=rt_mx) rt=x,rt_mx=mx[x];
int u;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
get_rt(All,x,u);
}
} void solve(int x,int All){
int u,Rt;
rt=-1,rt_mx=n;
get_sz(0,x);
get_rt(All,0,x);
vis[rt]=1;
Rt=rt; //处理上面的部分(如果有的话)
if (Rt!=x)
solve(x,All-sz[Rt]); //记录子树中的点并排好序
rec[0]=0;
for (int i=h[Rt];i!=-1;i=a[i].nxt)
if (!vis[a[i].y]) dfs(x,a[i].y);
sort(rec+1,rec+1+rec[0],cmp); //记录祖先(本身处理出来就是有序的了)
rec_pre[0]=1; rec_pre[1]=Rt;
for (int i=Rt;i!=x;i=pre[i]){
if (dis[Rt]-dis[pre[i]]<=l[Rt])
f[Rt]=min(f[Rt],f[pre[i]]+p[Rt]*(dis[Rt]-dis[pre[i]])+q[Rt]);
rec_pre[++rec_pre[0]]=pre[i];
} //统计祖先对子树中点的贡献
int top=0,tot=1;
for (int i=1;i<=rec_pre[0];++i){
while (tot<=rec[0]&&dis[rec[tot]]-dis[rec_pre[i]]>l[rec[tot]])
update(rec[tot],top),++tot;
while (top>1&&get_k(st[top-1],rec_pre[i])<=get_k(st[top],rec_pre[i])) //down
--top;
st[++top]=rec_pre[i];
}
while (tot<=rec[0]) update(rec[tot],top),++tot; //递归处理子树
for (int i=h[Rt];i!=-1;i=a[i].nxt){
if (!vis[a[i].y])
solve(a[i].y,sz[a[i].y]);
}
} void dfs(int fa,int x){
int u;
rec[++rec[0]]=x;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
dfs(x,u);
}
} void update(int i,int top){
if (!top) return;
int l=1,r=top-1,mid,ret=top,j,k;
while (l<=r){
mid=l+r>>1;
j=st[mid];
k=st[mid+1];
if ((f[j]-f[k])<=p[i]*(dis[j]-dis[k])) ret=mid,r=mid-1;
else l=mid+1;
}
ret=st[ret];
f[i]=min(f[i],f[ret]+p[i]*(dis[i]-dis[ret])+q[i]);
}

【bzoj3672】购票的更多相关文章

  1. 2017-12 CDQZ集训(已完结)

    从联赛活了下来(虽然分数倒一……),接下来要去CDQZ集训啦…… DAY -2 2017-12-16 被老师安排负责一部分同学的住宿以及安排…… 抓紧时间继续学习,LCT真好玩啊真好玩…… 晚上放假了 ...

  2. [BZOJ3672][UOJ#7][NOI2014]购票

    [BZOJ3672][UOJ#7][NOI2014]购票 试题描述  今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会.       ...

  3. bzoj3672【NOI2014】购票

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

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

    [BZOJ3672][NOI2014]购票(线段树,斜率优化,动态规划) 题解 首先考虑\(dp\)的方程,设\(f[i]\)表示\(i\)的最优值 很明显的转移\(f[i]=min(f[j]+(de ...

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

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

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

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

  7. [Noi2014]购票 BZOJ3672 点分治+斜率优化+CDQ分治

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

  8. 【BZOJ-3672】购票 树分治 + 斜率优化DP

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

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

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

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

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

随机推荐

  1. 微信小程序和微信H5测试中易出Bug的点和注意事项

    一.微信小程序 易出Bug的点: 小程序的分享转发功能 背景:小程序项目开发基本完毕也都已经测过几轮,功能上基本没有什么问题,但是上线后却被客户发现通过分享转发小程序给别人,别人无法正常打开的情况 原 ...

  2. IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步IO、同步IO

    最近看到OVS用户态的代码,在接收内核态信息的时候,使用了Epoll多路复用机制,对其十分不解,于是从网上找了一些资料,学习了一下<UNIX网络变成卷1:套接字联网API>这本书对应的章节 ...

  3. XSS 注入检查点

    如果你有个论坛,一般你会很注意用户发帖的注入问题,往往这个地方不会被注入,因为开发特别照顾.原则上XSS都是用户输入的,但是许多边角还是容易忽略.枚举一些检查点. 分页 分页通用组件获取url,修改p ...

  4. AngularJS - 路由 routing 基础示例

    AngularJS 路由 routing 能够从页面的一个视图跳转到另外一个视图,对单页面应用来讲是至关重要的.当应用变得越来越复杂时,我们需要一个合理的方式来管理用户在使用过程中看到的界面.Angu ...

  5. Polycarp and Letters(set首战!)

    Description Polycarp loves lowercase letters and dislikes uppercase ones. Once he got a string s con ...

  6. oracle数据库 expdp/impdp 和 exp/imp

    --EXPDP导出,需要系统用户权限,一般不使用--sqlplus--1.创建dmp导出逻辑目录 create directory 目录名 as '目录路径' create directory exp ...

  7. struts-resultType属性

    1.默认dispatcher:forward方式,服务器端跳转 2.redirect:客户端跳转 3.chain:Action转发,forward方式,服务器端跳转action 4.redirectA ...

  8. Alpha 冲刺(8/10)

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭鸭鸭鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 多次测试软件运行 学习OPENMP ...

  9. 404 Note Found队——现场编程

    目录 组员职责分工 github 的提交日志截图 程序运行截图 程序运行环境 GUI界面 基础功能实现 运行视频 LCG算法 过滤(降权)算法 算法思路 红黑树 附加功能一 背景 实现 附加功能二(迭 ...

  10. 《我是IT小小鸟》读笔

    兴趣是第一原则.一定要根据自己的兴趣确定发展方向,不要盲目从众和跟风.没有一个人的经历是可以复制的,多思考,不要照搬他人的做法,学习一下想法还是可以的,具体方法因人而异.学习软件技术时,不仅在知识节点 ...