版权声明:本人版权意识薄弱,请随意转载 https://blog.csdn.net/Ike940067893/article/details/88319202
前言
刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长。
BZOJ 3672[NOI2014]购票
- 中文题面,题意略: BZOJ 3672[NOI2014]购票
- 设 表示 点所花的最小费用,可以写出方程式 其中 是 的祖先且
- 显然可以斜率优化。那么我们来想想如何在树上做斜率优化。方法就是树链剖分后用 序建一颗线段树。线段树的每一个节点上用 维护这个区间中所有点形成的下凸包。从根往下 ,在 同时维护一个栈来存从根到当前点 的链上的点,这些点都有可能转移到当前点。那么只需要在这条链上二分出深度最小且满足 的点 ,在线段树中 的所有凸包里,二分查询答案就行。树链剖分+线段树+凸包二分,时间复杂度 。
-
CODE
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN = 200005; const LL inf = 1e18; int n, fa[MAXN], S[MAXN], indx; int fir[MAXN], to[MAXN], w[MAXN], nxt[MAXN], cnt; LL dis[MAXN], f[MAXN], P[MAXN], Q[MAXN], L[MAXN]; vector<int> t[MAXN<<2]; int sz[MAXN], hson[MAXN], top[MAXN], dfn[MAXN], seq[MAXN], tmr; template<typename T>inline void read(T &num) { char ch; while((ch=getchar())<'0'||ch>'9'); for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar()); } inline void Addedge(int u, int v, int wt) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; w[cnt] = wt; } inline void dfs(int u) { sz[u] = 1; for(int i = fir[u]; i; i = nxt[i]) { dis[to[i]] = dis[u] + w[i]; dfs(to[i]); sz[u] += sz[to[i]]; if(sz[to[i]] > sz[hson[u]]) hson[u] = to[i]; } } inline void dfs2(int u, int tp) { top[u] = tp; dfn[u] = ++tmr; seq[tmr] = u; if(hson[u]) dfs2(hson[u], tp); for(int i = fir[u]; i; i = nxt[i]) if(to[i] != hson[u]) dfs2(to[i], to[i]); } inline double Slope(int i, int j) { return (double)(f[i] - f[j]) / (dis[i] - dis[j]); } inline void Modify(int i, int l, int r, int x) { int sz = t[i].size(); while(sz > 1 && Slope(seq[x], t[i][sz-2]) < Slope(t[i][sz-1], t[i][sz-2])) t[i].pop_back(), --sz; t[i].push_back(seq[x]); if(l == r) return; int mid = (l + r) >> 1; if(x <= mid) Modify(i<<1, l, mid, x); else Modify(i<<1|1, mid+1, r, x); } inline LL calc(vector<int>t, int i) { int l = 1, r = t.size()-1, mid, pos = 0; while(l <= r) { mid = (l + r + 1) >> 1; if(Slope(t[mid], t[mid-1]) < 1.0*P[i]) pos = mid, l = mid+1; else r = mid-1; } int j = t[pos]; return f[j] + (dis[i]-dis[j])*P[i] + Q[i]; } inline LL Query(int i, int l, int r, int x, int y, int id) { if(x <= l && r <= y) return calc(t[i], id); int mid = (l + r) >> 1; LL res = inf; if(x <= mid) res = min(res, Query(i<<1, l, mid, x, y, id)); if(y > mid) res = min(res, Query(i<<1|1, mid+1, r, x, y, id)); return res; } inline void solve(int i, int anc) { int u = fa[i]; f[i] = inf; while(top[u] != top[anc]) f[i] = min(f[i], Query(1, 1, n, dfn[top[u]], dfn[u], i)), u = fa[top[u]]; f[i] = min(f[i], Query(1, 1, n, dfn[anc], dfn[u], i)); } inline void dp(int u) { S[++indx] = u; if(u > 1) { int l = 1, r = indx-1, mid; while(l < r) { mid = (l + r) >> 1; if(dis[u] - dis[S[mid]] <= L[u]) r = mid; else l = mid+1; } solve(u, S[l]); } Modify(1, 1, n, dfn[u]); for(int i = fir[u]; i; i = nxt[i]) dp(to[i]); --indx; } int main () { int type; read(n), read(type); for(int i = 2, x; i <= n; ++i) { read(fa[i]), read(x), read(P[i]), read(Q[i]), read(L[i]); Addedge(fa[i], i, x); } dfs(1); dfs2(1, 1); dp(1); for(int i = 2; i <= n; ++i) printf("%lld\n", f[i]); }
BZOJ 2402 陶陶的难题II
- 题意略: 2402: 陶陶的难题II
- 想想在长度为
序列上如何求这个最大值。暴力是
的。
设最终得到最大比值为 。那么就有 对任意 都满足,且存在至少一组 使等式取等。 - 设等式左边的最大值为 ,那么对于任意取值 ,有
- 显然我们可以二分,每次求最大值 就行了。那么等式左边的最大值只用分别算 和 的最大值再加起来。计算最大值时就是用斜率优化,维护一个上凸包。在凸包上二分求最值就行了。
- 而转移到树上就像上一道题一样在 序上用线段树维护凸包就行了。
- 这道题 线段树时需要把子树上传来的两个凸包合并,注意往凸包里加点时要保证 坐标递增。
- 时间复杂度 ,能过真是奇迹
-
CODE
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN = 300005; const double inf = 1e16; const double eps = 1e-10; int n; double x[MAXN][2], y[MAXN][2]; int to[MAXN<<1], nxt[MAXN<<1], fir[MAXN], cnt; int sz[MAXN], top[MAXN], fa[MAXN], hson[MAXN], dfn[MAXN], tmr, seq[MAXN], dep[MAXN]; inline void read(int &num) { char ch; while((ch=getchar())<'0'||ch>'9'); for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar()); } inline void addedge(int u, int v) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; } void dfs(int u, int ff) { fa[u] = ff; sz[u] = 1; dep[u] = dep[fa[u]] + 1; for(int i = fir[u]; i; i = nxt[i]) if(to[i] != fa[u]) { dfs(to[i], u), sz[u] += sz[to[i]]; if(sz[to[i]] > sz[hson[u]]) hson[u] = to[i]; } } void dfs2(int u, int tp) { top[u] = tp; dfn[u] = ++tmr; seq[tmr] = u; if(hson[u]) dfs2(hson[u], tp); for(int i = fir[u]; i; i = nxt[i]) if(to[i] != fa[u] && to[i] != hson[u]) dfs2(to[i], to[i]); } struct SegmentTree { vector<int> vec[MAXN<<2]; bool flg; inline double slope(const int &i, const int &j) { return (y[i][flg]-y[j][flg]) / (x[i][flg]-x[j][flg]); } inline bool Turn_left(const int &i, const int &j, const int &k) { register double a = x[j][flg] - x[i][flg], b = y[j][flg] - y[i][flg]; register double c = x[k][flg] - x[i][flg], d = y[k][flg] - y[i][flg]; return a*d + eps > b*c; } inline void Merge(vector<int> &h, const vector<int> &h1, const vector<int> &h2) { vector<int>::const_iterator i, j; i = h1.begin(), j = h2.begin(); int top = 0; while(i != h1.end() || j != h2.end()) { //下面的比较大小就是保证x递增 int p = i == h1.end() ? *j++ : j == h2.end() ? *i++ : x[*i][flg] < x[*j][flg] ? *i++ : *j++; while(top >= 2 && Turn_left(h[top-2], h[top-1], p)) h.pop_back(), --top; h.push_back(p), ++top; } } void build(int i, int l, int r) { if(l == r) { vec[i].push_back(seq[l]); return; } register int mid = (l + r) >> 1; build(i<<1, l, mid); build(i<<1|1, mid+1, r); Merge(vec[i], vec[i<<1], vec[i<<1|1]); } inline double calc(const vector<int> &V, const double &now) { register int l = 1, r = V.size()-1, mid, pos = V[0]; while(l <= r) { mid = (l + r) >> 1; if(slope(V[mid-1], V[mid]) + eps > now) pos = V[mid], l = mid+1; else r = mid-1; } return y[pos][flg] - now*x[pos][flg]; } double Query(const int &i, const int &l, const int &r, const int &L, const int &R, const double &now) { if(L <= l && r <= R) return calc(vec[i], now); int mid = (l + r) >> 1; register double res = -inf; if(L <= mid) res = max(res, Query(i<<1, l, mid, L, R, now)); if(R > mid) res = max(res, Query(i<<1|1, mid+1, r, L, R, now)); return res; } }T[2]; inline double check(int x, int y, const double &now, const bool &flg) { register double res = -inf; register int fx = top[x], fy = top[y]; while(fx != fy) { if(dep[fx] < dep[fy]) swap(x, y), swap(fx, fy); res = max(res, T[flg].Query(1, 1, n, dfn[top[x]], dfn[x], now)); x = fa[fx], fx = top[x]; } if(dep[x] < dep[y]) swap(x, y); res = max(res, T[flg].Query(1, 1, n, dfn[y], dfn[x], now)); return res; } inline int lca(int u, int v) { while(top[u] != top[v]) { if(dep[top[u]] > dep[top[v]]) u = fa[top[u]]; else v = fa[top[v]]; } return dep[u] > dep[v] ? v : u; } inline bool cmp0(const int &i, const int &j) { return x[i][0] < x[j][0]; } inline bool cmp1(const int &i, const int &j) { return x[i][1] < x[j][1]; } inline int dcmp(double x) { if(fabs(x) < eps) return 0; if(x > 0) return 1; return -1; } int main () { read(n); T[1].flg = 1; for(int i = 1; i <= n; ++i) scanf("%lf", &x[i][0]); for(int i = 1; i <= n; ++i) scanf("%lf", &y[i][0]); for(int i = 1; i <= n; ++i) scanf("%lf", &x[i][1]); for(int i = 1; i <= n; ++i) scanf("%lf", &y[i][1]); for(int i = 1, a, b; i < n; ++i) read(a), read(b), addedge(a, b), addedge(b, a); dfs(1, 0); dfs2(1, 1); T[0].build(1, 1, n); T[1].build(1, 1, n); int m, a, b; read(m); while(m--){ read(a), read(b); double l = 0, r = 1e8, mid; while(r - l > 1e-5) { mid = (l + r) / 2; if(dcmp(check(a, b, mid, 0) + check(a, b, mid, 1)) >= 0) l = mid; else r = mid; } printf("%.5f\n", l); } }
- 本人是大常数选手, 卡过 。