BZOJ 3672 购票
2015-06-28 23:01
267 查看
Description
今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国\(n\)个城市的OIer们都会从各地出发,到SZ市参加这次盛会。
全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的\(n\)个城市用\(1\)到\(n\)的整数编号。其中SZ市的编号为\(1\)。对于除SZ市之外的任意一个城市\(v\),我们给出了它在这棵树上的父亲城市\(f_{v}\)以及到父亲城市道路的长度\(s_{v}\)。
从城市\(v\)前往SZ市的方法为:选择城市\(v\)的一个祖先\(a\),支付购票的费用,乘坐交通工具到达\(a\)。再选择城市\(a\)的一个祖先\(b\),支付费用并到达\(b\)。以此类推,直至到达SZ市。
对于任意一个城市\(v\),我们会给出一个交通工具的距离限制\(l_{v}\)。对于城市\(v\)的祖先\(a\),只有当它们之间所有道路的总长度不超过\(lv\)时,从城市\(v\)才可以通过一次购票到达城市\(a\),否则不能通过一次购票到达。对于每个城市\(v\),我们还会给出两个非负整数\(p_{v},q_{v}\)作为票价参数。若城市\(v\)到城市\(a\)所有道路的总长度为\(d\),那么从城市\(v\)到城市 \(a\)购买的票价为\(d \times p_{v}+q_{v}\)。
每个城市的OIer都希望自己到达SZ市时,用于购票的总资金最少。你的任务就是,告诉每个城市的OIer他们所花的最少资金是多少。
Input
第\(1\)行包含\(2\)个非负整数\(n,t\),分别表示城市的个数和数据类型(其意义将在后面提到)。输入文件的第\(2\)到\(n\)行,每行描述一个除SZ之外的城市。其中第\(v\)行包含\(5\)个非负整数\(f_{v},s_{v},p_{v},q_{v},l_{v}\),分别表示城市\(v\)的父亲城市,它到父亲城市道路的长度,票价的两个参数和距离限制。请注意:输入不包含编号为\(1\)的SZ市,第\(2\)行到第 n 行分别描述的是城市\(2\)到城市\(n\)。
Output
输出包含\(n-1\)行,每行包含一个整数。其中第\(v\)行表示从城市\(v+1\)出发,到达SZ市最少的购票费用。同样请注意:输出不包含编号为\(1\)的SZ市。
Sample Input
7 3
1 2 20 0 3
1 5 10 100 5
2 4 10 10 10
2 9 1 100 10
3 5 20 100 10
4 4 20 0 10
Sample Output
40
150
70
149
300
150
Hint
首先另\(d_{i}\)表示\(i\)的\(1\)的距离,我们可以先写出dp方程\[f_{i} = min \lbrace f_{j}+(d_{i}-d_{j}P_{i}+Q_{j}) \rbrace\]
然后化简一下就可得到\[f_{i}=min \lbrace f_{j}+P_{i}d_{i}+q_{i}-P_{i}d_{j} \rbrace\]
令\[x = d_{j},y = f_{j}+P_{i}d_{}+Q_{i},k = P_{i},b = f_{i}\]就是标准的斜率优化\(b=y-kx\)的式子了。维护一个下凸壳即可。
于是考虑如何维护这个下凸壳,由于对\(i\)有用的\(j\)一定是\(i\)的祖先,所以明显从上往下更新,于是就有树链剖分。
但是好像不好动态维护凸壳,但是我们发现从上往下加点\(x=d_{i}\)是递增的,所以没必要二分,直接叉积就行了。
至于满足\(L_{i}\)的限制,直接在树上用树链剖分找出一个满足条件的最高点,再做询问就没问题了。时间复杂度\(O(nlog^{3}n)\)达不到上界。
#include<iostream> #include<cmath> #include<vector> #include<cstdio> #include<cstdlib> using namespace std; typedef long long ll; #define eps (1e-6) #define maxn (400010) int N,side[maxn],toit[maxn],next[maxn],cnt,size[maxn],id[maxn],father[maxn]; int top[maxn]; ll len[maxn],d[maxn],f[maxn],Map[maxn],P[maxn],Q[maxn],L[maxn]; struct node { ll x,y; friend inline int operator /(const node &a,const node &b) { double res = 1.0*a.x*b.y-1.0*b.x*a.y; if (fabs(res-0) <= eps) return 0; else if (res > 0) return 1; else return -1; } friend inline node operator -(const node &a,const node &b) { return (node){a.x-b.x,a.y-b.y}; } inline double angle() { return 1.0*y/x; } }; vector <node> convex[maxn*2]; vector <double> slop[maxn*2]; inline void add(int a,int b,ll c) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; len[cnt] = c; } inline void ins(int a,int b,ll c) { add(a,b,c); add(b,a,c); } inline ll read() { char ch; ll ret = 0,f = 1; do ch = getchar(); while (!(ch >= '0'&&ch <= '9')&&ch != '-'); if (ch == '-') ch = getchar(),f = -1; do ret = ret*10+(ll)(ch-'0'),ch = getchar(); while (ch >= '0'&&ch <= '9'); return f*ret; } inline void dfs(int now) { size[now] = 1; for (int i = side[now];i;i = next[i]) { if (toit[i] == father[now]) continue; father[toit[i]] = now; d[toit[i]] = d[now]+len[i]; dfs(toit[i]); size[now] += size[toit[i]]; } } inline void Div(int now,int Top) { top[now] = Top; id[now] = ++cnt; Map[cnt] = now; int heavy = 0; for (int i = side[now];i;i = next[i]) if (toit[i] != father[now]&&size[toit[i]] > size[heavy]) heavy = toit[i]; if (!heavy) return; Div(heavy,Top); for (int i = side[now];i;i = next[i]) if (toit[i] != father[now]&&toit[i] != heavy) Div(toit[i],toit[i]); } inline ll br(int now,ll key) { int l = 0,r = slop[now].size()-1,mid; while (l <= r) { mid = (l+r) >> 1; if (slop[now][mid] < key) l = mid+1; else r = mid-1; } return convex[now][l].y-key*convex[now][l].x; } inline ll query(int now,int l,int r,int ql,int qr,ll key) { if (ql <= l&&r <= qr) return br(now,key); int mid = (l+r)>>1; if (qr <= mid) return query(now<<1,l,mid,ql,qr,key); else if (ql > mid) return query(now<<1|1,mid+1,r,ql,qr,key); else return min(query(now<<1,l,mid,ql,mid,key),query(now<<1|1,mid+1,r,mid+1,qr,key)); } inline void insert(int now,int l,int r,int pos,const node &pp) { int nn = convex[now].size(); while (nn>1&&(pp-convex[now][nn-1])/(convex[now][nn-1]-convex[now][nn-2]) >= 0) --nn,convex[now].pop_back(),slop[now].pop_back(); ++nn; convex[now].push_back(pp); if (nn > 1) slop[now].push_back((convex[now][nn-1]-convex[now][nn-2]).angle()); if (l == r) return; int mid = (l+r) >> 1; if (pos <= mid) insert(now<<1,l,mid,pos,pp); else insert(now<<1|1,mid+1,r,pos,pp); } inline void work(int now) { int Top = now,i; f[now] = 1LL<<60; while (true) { if (L[now] >= d[Top]-d[father[top[Top]]]) L[now] -= d[Top]-d[father[top[Top]]],Top = father[top[Top]]; else { int l = id[top[Top]],r = id[Top],mid; while (l <= r) { mid = (l+r)>>1; if (d[Top]-d[Map[mid]] > L[now]) l = mid+1; else r = mid-1; } Top = Map[l]; break; } } if (Top == now) return; for (i = father[now];top[i] != top[Top];i = father[top[i]]) f[now] = min(query(1,1,N,id[top[i]],id[i],P[now]),f[now]); f[now] = min(query(1,1,N,id[Top],id[i],P[now]),f[now]); f[now] += P[now]*d[now]+Q[now]; } inline void Dfs(int now) { work(now); insert(1,1,N,id[now],(node){d[now],f[now]}); for (int i = side[now];i;i = next[i]) if (toit[i] != father[now]) Dfs(toit[i]); } int main() { freopen("3672.in","r",stdin); freopen("3672.out","w",stdout); N = read(); read(); for (int i = 2,a,b;i <= N;++i) { a = read(),b = read(),ins(i,a,b); P[i] = read(),Q[i] = read(),L[i] = read(); } d[cnt = 0] = -1LL << 60; dfs(1); Div(1,1); insert(1,1,N,id[1],(node){0,0}); for (int i = side[1];i;i = next[i]) Dfs(toit[i]); for (int i = 2;i <= N;++i) printf("%lld\n",f[i]); fclose(stdin); fclose(stdout); return 0; }
相关文章推荐
- rsync同步数据
- 国家能源局:《电力企业网络与信息安全专项监管报告》
- 国家能源局:《电力企业网络与信息安全专项监管报告》
- 黑马程序员——Java基础——IO流
- 如何控制jquery的ready事件
- LIVE555源码研究之四:MediaServer (一)
- drupal THEME主要文件
- ls目录结构
- WEB_容器_tomcat_本地运行源码_审
- 1011--映射一对多关联关系(.单向 n-1)
- java中调整字体
- Spring 配置log4j和简单介绍Log4J的使用
- 编译器错误消息: CS0234: 命名空间“System.Web”中不存在类型或命名空间名称“Optimization”(是否缺少程序集引用?)
- iOS面试题01
- Java 中基本类型和包装类之间的转换
- web系统架构设计中需要知道的点(前端篇)
- Linux下磁盘管理
- Node.js学习-----------搭建一个简单的HTTP服务器
- 第4天-sql通配符与正则式
- ViewPager,OnPageChangeListener及PageTransformer