【题目链接】
【思路要点】
- 先树链剖分,然后用李超线段树维护序列,支持一段区间对某一次函数取Min,以及查询区间最小值。
- 由于需要区间定位,单次1号操作的时间复杂度为\(O(Log^3N)\),单次2号操作的时间复杂度为\(O(Log^2N)\)。
- 总时间复杂度\(O(MLog^3N)\),但数据没有将该做法的复杂度卡满,实际上做到这一点也很难,因此该做法可以通过本题,甚至跑得很快。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXP = 200005; const long long INF = 123456789123456789ll; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct SegmentTree { struct Node { int lc, rc; long long k, b, Min; } a[MAXP]; int root, size, n; long long pos[MAXN]; void build(int &root, int l, int r) { root = ++size; a[root].k = 0; a[root].b = INF; a[root].Min = INF; if (l == r) return; int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); } void init(int x) { n = x; root = size = 0; build(root, 1, n); } void update(int root, int l, int r) { a[root].Min = min(a[root].k * pos[l] + a[root].b, a[root].k * pos[r] + a[root].b); if (a[root].lc) chkmin(a[root].Min, a[a[root].lc].Min); if (a[root].rc) chkmin(a[root].Min, a[a[root].rc].Min); } void insert(int root, int l, int r, int ql, int qr, long long k, long long b) { if (a[root].k * pos[l] + a[root].b <= k * pos[l] + b && a[root].k * pos[r] + a[root].b <= k * pos[r] + b) return; if (l == ql && r == qr) { if (a[root].k * pos[l] + a[root].b >= k * pos[l] + b && a[root].k * pos[r] + a[root].b >= k * pos[r] + b) { a[root].k = k; a[root].b = b; update(root, l, r); return; } int mid = (l + r) / 2; if (a[root].k * pos[mid] + a[root].b > k * pos[mid] + b) { swap(a[root].k, k); swap(a[root].b, b); } insert(a[root].lc, l, mid, ql, mid, k, b); insert(a[root].rc, mid + 1, r, mid + 1, qr, k, b); update(root, l, r); return; } int mid = (l + r) / 2; if (ql <= mid) insert(a[root].lc, l, mid, ql, min(mid, qr), k, b); if (mid + 1 <= qr) insert(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, k, b); update(root, l, r); } void insert(int l, int r, long long k, long long b) { b -= pos[l] * k; insert(root, 1, n, l, r, k, b); } long long query(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].Min; int mid = (l + r) / 2; long long ans = min(a[root].k * pos[ql] + a[root].b, a[root].k * pos[qr] + a[root].b); if (mid >= ql) chkmin(ans, query(a[root].lc, l, mid, ql, min(mid, qr))); if (mid + 1 <= qr) chkmin(ans, query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr)); return ans; } long long query(int l, int r) { return query(root, 1, n, l, r); } } ST; struct edge {int dest, len; }; vector <edge> a[MAXN]; int n, m, timer, up[MAXN], dfn[MAXN]; int depth[MAXN], father[MAXN], size[MAXN], son[MAXN]; long long tsum, lastpos, toroot[MAXN]; void efs(int pos, int fa) { dfn[pos] = ++timer; up[pos] = fa; ST.pos[timer] = tsum + toroot[pos] - lastpos; if (son[pos]) efs(son[pos], fa); else tsum += toroot[pos] - lastpos; for (unsigned i = 0; i < a[pos].size(); i++) if (a[pos][i].dest != father[pos] && a[pos][i].dest != son[pos]) { lastpos = toroot[pos]; efs(a[pos][i].dest, a[pos][i].dest); } } void dfs(int pos, int fa) { size[pos] = 1; father[pos] = fa; depth[pos] = depth[fa] + 1; for (unsigned i = 0; i < a[pos].size(); i++) if (a[pos][i].dest != fa) { toroot[a[pos][i].dest] = toroot[pos] + a[pos][i].len; dfs(a[pos][i].dest, pos); size[pos] += size[a[pos][i].dest]; if (size[a[pos][i].dest] > size[son[pos]]) son[pos] = a[pos][i].dest; } } int lca(int x, int y) { while (up[x] != up[y]) { if (depth[up[x]] < depth[up[y]]) swap(x, y); x = father[up[x]]; } if (depth[x] < depth[y]) return x; else return y; } long long dist(int x, int y) { return toroot[x] + toroot[y] - 2 * toroot[lca(x, y)]; } void insert(int x, int y, long long k, long long b) { long long dx = 0, dy = dist(x, y); while (up[x] != up[y]) { if (depth[up[x]] > depth[up[y]]) { dx += toroot[x] - toroot[up[x]]; ST.insert(dfn[up[x]], dfn[x], -k, b + dx * k); dx += toroot[up[x]] - toroot[father[up[x]]]; x = father[up[x]]; } else { dy -= toroot[y] - toroot[up[y]]; ST.insert(dfn[up[y]], dfn[y], k, b + dy * k); dy -= toroot[up[y]] - toroot[father[up[y]]]; y = father[up[y]]; } } if (dfn[x] < dfn[y]) ST.insert(dfn[x], dfn[y], k, b + dx * k); else ST.insert(dfn[y], dfn[x], -k, b + dy * k); } long long query(int x, int y) { long long ans = INF; while (up[x] != up[y]) { if (depth[up[x]] < depth[up[y]]) swap(x, y); chkmin(ans, ST.query(dfn[up[x]], dfn[x])); x = father[up[x]]; } if (dfn[x] > dfn[y]) swap(x, y); chkmin(ans, ST.query(dfn[x], dfn[y])); return ans; } int main() { read(n), read(m); ST.init(n); for (int i = 1; i <= n - 1; i++) { int x, y, z; read(x), read(y), read(z); a[x].push_back((edge) {y, z}); a[y].push_back((edge) {x, z}); } dfs(1, 0); efs(1, 1); for (int i = 1; i <= m; i++) { int opt, x, y; read(opt), read(x), read(y); if (opt == 1) { long long b, k; read(k), read(b); insert(x, y, k, b); } else writeln(query(x, y)); } return 0; }