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. Jmeter接口测试之Get请求

    [一] 在测试计划下面添加一个线程组---------->在线程组下面分别添加HTTP请求.响应断言.BeanShellPreProcessor.察看结果树.聚合报告等内容. [二] 将使用的协 ...

  2. 近中期3D编程研究目标

    近几年一直在用业余时间研究3D编程,研究的中期目标是建立一个实用的开源3D编程框架.3D编程技术最直接的应用是开发游戏,所以3D编程框架也就是3D游戏开发框架.在我看来,游戏是否好玩的关键是能否为玩家 ...

  3. Phaser3 场景Scene之间的传值 -- HTML JAVASCRIPT 网页游戏开发

      PHASERJS3 一.首先当然得有至少有二个场景sceneA.js,sceneB.js 二.从场景A传值到场景B二种方法 1)通过事件this.events.emit('event key',{ ...

  4. Hackerank-Array-NewYearChaos

    题目背景描述 新年第一天,N 个人排队坐过山车.每个人穿有带编号的衣服 \([1, 2, 3, ...]\). 因为排队时间太久,有人发现给前面相邻的人喂一颗糖,就可以和他交换位置,而每人手里只有两颗 ...

  5. spring JDBC 事务管理

    spring JDBC 事务管理 一.Spring 中的JDBC Spring中封装了JDBC的ORM框架,可以用它来操作数据,不需要再使用外部的OEM框架(MyBatis),一些小的项目用它. 步骤 ...

  6. conda环境管理

    查看环境 conda env list 创建环境 conda create -n python36 python=3.6 进入环境 source activate python36 activate ...

  7. eos对数据库的操作

    eosio的multi_index 概述 multi_index是eosio上的数据库管理接口,通过eosio::multi_index智能合约能够写入.读取和修改eosio数据库的数据 multi_ ...

  8. 第九次ScrumMeeting博客

    第九次ScrumMeeting博客 本次会议于11月4日(六)22时整在3公寓725房间召开,持续20分钟. 与会人员:刘畅.辛德泰.窦鑫泽.张安澜.赵奕.方科栋. 1. 每个人的工作(有Issue的 ...

  9. Live Archive 训练题

    7091 Height Ordering Mrs. Chambers always has her class line up in height order (shortest at the fro ...

  10. Python Pygame(5)绘制基本图形

    最近很火一些简单图形构成的小游戏,这里介绍一些绘制图形的函数. 1.绘制矩形 rect(Surface,color,Rect,width=0) 第一个参数指定矩形绘制到哪个Surface对象上 第二个 ...