Noi2014 购票

传送门

另一个传送门

这题劲啊……

其实这题题解应该都烂大街了,不过我还是想大概写一下……就当是留作以后复习用也好……

$t=0$的斜率优化很好搞。

到了$t=1$的情况时,每个点都有转移区间的限制,做法大致就有线段树维护凸壳和分治两种,线段树维护凸壳没写过+太麻烦不想写,所以分治好了。把序列等分成两半之后考虑用左半更新右半,显然可以把右半按转移区间左端点排序,然后倒着加左边的点并处理右边的询问即可,因为需要在凸壳上二分,因此复杂度为$O(n\log^2n)$。

对于$t=3$的情况,把序列上的分治换成树分治即可。如果用点分治的话,每次找到重心之后先递归重心以上的部分,再在连通块内暴力得出重心的$f$,然后用重心到连通块最上方节点组成凸壳并更新重心以下各节点,最后递归重心以下各子树即可。当然还是要把各子树的点按转移左端点排序并逆序加点维护,复杂度仍然$O(n\log^2n)$。

 1 /**************************************************************
 2     Problem: 3672
 3     User: hzoier
 4     Language: C++
 5     Result: Accepted
 6     Time:9748 ms
 7     Memory:17924 kb
 8 ****************************************************************/
 9 #include<cstdio>
10 #include<cstring>
11 #include<algorithm>
12 #include<vector>
13 using namespace std;
14 const int maxn=200010;
15 const long double eps=1e-12;
16 void dfs(int);
17 void solve(int);
18 int getcenter(int);
19 int bfs(int);
20 int getpoint(long long);
21 long double getk(int,int);
22 bool cmp(int,int);
23 vector<int>G[maxn];
24 int p[maxn],size[maxn],son[maxn],s[maxn],top,q[maxn],vis[maxn]={0},tim=0;
25 long long f[maxn],d[maxn],b[maxn],l[maxn];
26 int n,a[maxn];
27 int main(){
28     memset(f,63,sizeof(f));
29     scanf("%d%*d",&n);
30     for(int i=2;i<=n;i++){
31         scanf("%d%lld%d%lld%lld",&p[i],&d[i],&a[i],&b[i],&l[i]);
32         G[p[i]].push_back(i);
33         d[i]+=d[p[i]];
34     }
35     f[1]=0;
36     solve(1);
37     for(int i=2;i<=n;i++)printf("%lld\n",f[i]);
38     return 0;
39 }
40 void solve(int x){
41     x=bfs(x);
42     vis[x]=++tim;
43     if(p[x]&&!vis[p[x]]){
44         int u=x;
45         while(p[u]&&!vis[p[u]])u=p[u];
46         solve(u);
47         for(u=p[x];u&&(!vis[u]||vis[u]>vis[x])&&d[u]>=d[x]-l[x];u=p[u])f[x]=min(f[x],f[u]+a[x]*(d[x]-d[u])+b[x]);
48     }
49     bfs(x);
50     sort(q+1,q+size[x],cmp);
51     int i=1;
52     while(i<size[x]&&d[x]<d[q[i]]-l[q[i]])i++;
53     s[top=1]=x;
54     for(x=p[x];i<size[q[0]];i++){
55         while(x&&(!vis[x]||vis[x]>vis[q[0]])&&d[x]>=d[q[i]]-l[q[i]]){
56             while(top>1&&getk(x,s[top])+eps>getk(s[top],s[top-1]))top--;
57             s[++top]=x;
58             x=p[x];
59         }
60         int j=getpoint(a[q[i]]);
61         f[q[i]]=min(f[q[i]],f[j]+a[q[i]]*(d[q[i]]-d[j])+b[q[i]]);
62     }
63     x=q[0];
64     for(int i=0;i<(int)G[x].size();i++)if(!vis[G[x][i]])solve(G[x][i]);
65 }
66 int bfs(int x){
67     int head=0,tail=0;
68     q[tail++]=x;
69     while(head!=tail){
70         x=q[head++];
71         size[x]=1;
72         son[x]=0;
73         for(int i=0;i<(int)G[x].size();i++)if(!vis[G[x][i]])q[tail++]=G[x][i];
74     }
75     for(int i=tail-1;i;i--){
76         size[p[q[i]]]+=size[q[i]];
77         if(size[q[i]]>size[son[p[q[i]]]])son[p[q[i]]]=q[i];
78     }
79     x=q[0];
80     int s=size[x];
81     while(son[x]&&(size[son[x]]<<1)>s)x=son[x];
82     return x;
83 }
84 int getpoint(long long k){
85     if(top<=1)return s[top];
86     int L=1,R=top-1;
87     while(L<=R){
88         int M=(L+R)>>1;
89         if(getk(s[M],s[M+1])-eps<k)R=M-1;
90         else L=M+1;
91     }
92     return s[L];
93 }
94 inline long double getk(int i,int j){return (long double)(f[i]-f[j])/(d[i]-d[j]);}
95 bool cmp(int x,int y){return d[x]-l[x]>d[y]-l[y];}

新技能:斜率优化上树get√

时间: 2024-08-03 14:05:03

Noi2014 购票的相关文章

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

3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 480  Solved: 212[Submit][Status][Discuss] Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其

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

[BZOJ3672][Noi2014]购票 Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其中SZ市的编号为 1.对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv. 从城市 v 前往SZ市的方法为:选择城

【bzoj3672】[Noi2014]购票 斜率优化+CDQ分治+树的点分治

题目描述 给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$.问从每个点到1花费的最小代价(中途可以经停其它点) 输入 第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到).输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市.其中第 v 行包含 5 个非负整数 $f_v,s_v,p_v,q_v,l_v$,分别表示城市 v 的父亲城市,它到

BZOJ3672: [Noi2014]购票

Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其中SZ市的编号为 1.对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv. 从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐

BZOJ3672/UOJ7 [Noi2014]购票

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用

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

试题描述 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其中SZ市的编号为 1.对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv. 从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到达

[NOI2014]购票 --- 斜率优化 + 树形DP + 数据结构

题目描述 今年夏天,NOI在SZ市迎来了她30周岁的生日. 来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接. 为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其中SZ市的编号为 1. 对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv 以及到父亲城市道路的长度 sv. 从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到

BZOJ 3672 NOI2014 购票

这题好神啊..好神啊...好神啊... 首先列出N2的DP方程较易. 从DP方程很容易看出来是斜率优化. 如何进一步优化? 考虑对当前点以上的链建立一个下凸包. 维护凸包就可以,但不是很好写. 观察到方程可以必然由它的祖先节点转移.很像Cash那道题. 尝试CDQ分治,每次先递归处理根所在的子树. 然后用根所在的子树,对当前点更新答案,对其他点进行根据dis-lim排序,维护栈即可. 考虑到复杂度,我们需要对树进行点分治. code: #include<iostream> #include&l

数据结构(树链剖分):NOI2014 购票

用线段树优化凸包. 注意以下细节: 1.必须先递归非重儿子,若先递归重儿子,可能会把有用解踢掉. 2.不能每条链只建一个凸包,因为凸包不能只用某一部分去更新答案(可能不在考虑范围中的点联合某点踢掉了最优的点). 还有就是老实地用double比斜率吧,不然爆long long. 1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 #inc