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. 获取json键值对的对应字符串

    获取json中的姓名 json串ac 关键字key public class Json { public static String json(String  key;String  ac) { JS ...

  2. NO.06--聊一聊“币”吧!

    近期博主更新的频率明显慢来 ,一来是最近的工作比较忙碌,几个项目几乎同时要上线.二来是在思考是不是把我平时生活中的一些事情写进来博客,不只是分享分享技术. 趁着区块链.比特币火爆,博主也算是略有涉猎, ...

  3. loadrunner socket协议问题归纳(5)

    获取服务器的返回值,可以用web_reg_save_param函数,该参数最好放到: 语法: int web_reg_save_param(const char *ParamName, <lis ...

  4. 基于DPDK的高效数据包捕获技术分析与应用

    被NFV的论文折磨了两天,今天上午看了两篇DPDK的综述. 传统的包捕获机制 1. BPF 两个组成部分:转发部分和过滤部分. 转发部分负责从链路层提取数据包并转发给过滤部分. 过滤部分根据过滤规则, ...

  5. Codeforces Round #287 (Div. 2) E. Breaking Good 最短路

    题目链接: http://codeforces.com/problemset/problem/507/E E. Breaking Good time limit per test2 secondsme ...

  6. jQuery之过滤选择器

    在原有选择器匹配的元素中进一步进行过滤的选择器 * 基本 * 内容 * 可见性 * 属性 需求 1. 选择第一个div 2. 选择最后一个class为box的元素 3. 选择所有class属性不为bo ...

  7. PAT 甲级 1020 Tree Traversals

    https://pintia.cn/problem-sets/994805342720868352/problems/994805485033603072 Suppose that all the k ...

  8. JAVA字节流(读写文件)

    InputStream此抽象类是表示字节输入流的所有类的超类.需要定义 InputStream 的子类的应用程序必须始终提供返回下一个输入字节的方法. int available()返回此输入流方法的 ...

  9. 常用的一些sql

    --根据某一列中包括的逗号将一行数据变多行 select a,c from (with test as (select 'abc' a,'1,2,3' c from dual e) select a, ...

  10. Ubuntu 16.04 LTS安装sogou输入法详解

    http://blog.csdn.net/qq_21792169/article/details/53152700 最近开始学习linux 在安装输入法中遇到的一些问题,最终成功安装,也得益于网络上的 ...