「NOI2014」购票

「NOI2014」购票

解题思路

先列出 \(dp\) 式子并稍微转化一下

\[dp[u] =\min(dp[v]+(dis[u]-dis[v]) \times p[u] + q[u])) \ \ \ \ (dis[v]-lim[u] \leq dis[u]) \\ dp[u]=\min(dp[v]+dis[v]\times p[u]) + p[u]\times dis[u]+q[u] \\ \]

假设有 \(dis(v2)< dis(v1)\)\(p(u)\)\(v2\) 的取值比 \(v1\) 优,可以得到斜率式

\[dp(v2)+p(u)\times dis(v2)\leq dis(v1)+p(u)\times dp(v1) \\ \dfrac{dp(v2) -dp(v1)}{dis(v2)-dis(v1)}\geq p(u) \]

利用 \(cdq\) 的思想对有根树进行点分治,每次计算分治中心 \(H\) 到其祖先的一条链对其它联通块的贡献,维护一个凸包在凸包上二分即可,还挺好写的,复杂度 \(\mathcal O(n\log^2n)\)

code

/*program by mangoyang*/ 
#include<bits/stdc++.h>
#define inf ((ll)(1e18))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define int ll
const int N = 2000005;
vector<int> g[N];
int dis[N], dep[N], lim[N], dp[N], d[N], Q[N], p[N], pa[N];
int A[N], sz[N], vis[N], pq[N], q[N], n, mn, rt, all, cnt;
inline double slope(int x, int y){ 
	return (double) (dp[y] - dp[x]) / (double) (dis[y] - dis[x]);
}
inline bool cmp(int x, int y){
	return dis[x] - lim[x] > dis[y] - lim[y];
}
inline void update(int x, int y){
	dp[x] = min(dp[y] + (dis[x] - dis[y]) * p[x] + q[x], dp[x]);
}
inline void prework(int u, int fa){
	pa[u] = fa, dep[u] = dep[fa] + 1, dis[u] = dis[fa] + d[u];
	for(int i = 0; i < (int) g[u].size(); i++)
		if(g[u][i] != fa) prework(g[u][i], u);
}
inline void getsize(int u, int fa){
	int mson = 0; sz[u] = 1;
	for(int i = 0; i < (int) g[u].size(); i++){
		int v = g[u][i];
		if(vis[v] || v == fa) continue;
		getsize(v, u), sz[u] += sz[v];
		if(sz[v] > mson) mson = sz[v];
	}
	mson = max(mson, all - sz[u]);
	if(mson < mn) mn = mson, rt = u;
}
inline void dfs(int u, int fa){
	A[++cnt] = u;
	for(int i = 0; i < (int) g[u].size(); i++)
		if(!vis[g[u][i]] && g[u][i] != fa) dfs(g[u][i], u);
}
inline void divtree(int u, int top){
	vis[u] = 1; int lst = all;
	for(int i = 0; i < (int) g[u].size(); i++){
		int v = g[u][i];
		if(!vis[v] && v == pa[u]){
			mn = all = sz[v] > sz[u] ? lst - sz[u] : sz[v];
			getsize(v, u), divtree(rt, top);
		}
	}	
	for(int s = pa[u]; s != pa[top]; s = pa[s]) 
		if(dis[u] - dis[s] <= lim[u]) update(u, s);
	cnt = 0;
	for(int i = 0; i < (int) g[u].size(); i++){
		int v = g[u][i];
		if(!vis[v]) dfs(v, u);
	}
	sort(A + 1, A + cnt + 1, cmp);
	int t = 0; int s = u;
	for(int i = 1; i <= cnt; i++){
		while(s != pa[top] && dis[A[i]] - lim[A[i]] <= dis[s]){
			while(t > 1 && slope(Q[t-1], Q[t]) <= slope(Q[t-1], s)) t--;
			Q[++t] = s, s = pa[s];
		}
		int l = 1, r = t - 1, res = 1;
		while(l <= r){
			int mid = (l + r) >> 1;
			if(slope(Q[mid], Q[mid+1]) >= (double) p[A[i]]) 
				res = l = mid + 1;
			else r = mid - 1;
		}
		if(res <= t) update(A[i], Q[res]);
	}
	for(int i = 0; i < (int) g[u].size(); i++){
		int v = g[u][i];
		if(vis[v]) continue;
		mn = all = sz[v] > sz[u] ? lst - sz[u] : sz[v];
		getsize(v, u), divtree(rt, v);
	}
}
signed main(){
	read(n); int type; read(type);
	for(int i = 2, x; i <= n; i++){
		read(x), read(d[i]), read(p[i]);
		read(q[i]), read(lim[i]), dp[i] = inf;
		g[x].push_back(i), g[i].push_back(x);
	}
	prework(1, 0);
	mn = all = n, getsize(1, 0), divtree(rt, 1);
	for(int i = 2; i <= n; i++) printf("%lld\n", dp[i]);
	return 0;
}
posted @ 2019-03-12 20:16  Joyemang33  阅读(242)  评论(0编辑  收藏  举报