初见安~这里是传送门:洛谷P4069 [SDOI2016]游戏
题解
这题真的是咕了好几个月了终于过了,然后又咕了几个星期才来写博客……
题意很简单,每次让你在一条路径上放一个等差数列,问你某点上数的最小值。
这就是一个李超线段树的模板题了。我们树上利用树剖开一棵线段树,维护每一段等差序列的最小值就好。
接下来看这一段一段的等差序列(直线)。
树上路径的经典性质——拆分成两段,和
。每次给定的a和b我们用k和b来替换。【
首先看前一段,设表示点i到根节点的距离,
路径上的点u将得到的数值是
,但是因为这条路是从下往上走的,而我们树剖的dfn序号是从上往下的,所以要反过来,所以这个等差序列应为:
也就是说,是个,
的直线。直接插入就好。【注意,dis_u是自变量
接下来看后一段,方向是顺着的所以可以直接写出来:
所以,
。
两种情况对应的解析式我们讨论出来了,接下来就是插入李超线段树然后该咋维护咋维护了。
上代码——
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 100005
using namespace std;
typedef long long ll;
const ll INF = 123456789123456789;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
int n, m;
struct edge {int to, w, nxt;} e[maxn << 1];
int head[maxn], k = 0;
void add(int u, int v, int w) {e[k] = {v, w, head[u]}; head[u] = k++;}
int size[maxn], dep[maxn], son[maxn], fa[maxn], top[maxn], dfn[maxn], raw[maxn], tot = 0;
ll dis[maxn];
struct HLD {//树剖基操打包
void dfs1(int u) {
size[u] = 1;
for(int i = head[u], v; ~i; i = e[i].nxt) {
v = e[i].to; if(v == fa[u]) continue;
dep[v] = dep[u] + 1, fa[v] = u, dis[v] = dis[u] + 1ll * e[i].w;
dfs1(v); size[u] += size[v];
if(size[v] > size[son[u]]) son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp; dfn[u] = ++tot, raw[tot] = u;
if(son[u]) dfs2(son[u], tp);
for(int i = head[u], v; ~i; i = e[i].nxt) {
v = e[i].to; if(v != fa[u] && v != son[u]) dfs2(v, v);
}
}
int LCA(int u, int v) {
while(top[u] != top[v]) {
if(dep[top[u]] > dep[top[v]]) swap(u, v);
v = fa[top[v]];
}
if(dep[u] > dep[v]) return v; return u;
}
}H;
struct TREE {ll k, b, minn;} t[maxn << 2];
void build(int p, int l, int r) {//建树
t[p].b = t[p].minn = INF;
if(l == r) return;
register int mid = l + r >> 1;
build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r);
}
void change(int p, int l, int r, int ls, int rs, ll k, ll b) {//李超线段树区间修改
register int mid = l + r >> 1;//有点繁琐 但是很好理解
if(ls <= l && r <= rs) {
ll l1 = t[p].k * dis[raw[l]] + t[p].b, r1 = t[p].k * dis[raw[r]] + t[p].b;
ll l2 = k * dis[raw[l]] + b, r2 = k * dis[raw[r]] + b;
if(l1 <= l2 && r1 <= r2) return;
if(l2 <= l1 && r2 <= r1) {t[p].k = k, t[p].b = b, t[p].minn = min(t[p].minn, min(l2, r2)); return;}
ll mx = dis[raw[mid]], pp = (t[p].b - b) / (k - t[p].k);
if(l1 < l2) {
if(pp <= mx) change(p << 1, l, mid, ls, rs, t[p].k, t[p].b), t[p].k = k, t[p].b = b;
else change(p << 1 | 1, mid + 1, r, ls, rs, k, b);
} else {
if(pp <= mx) change(p << 1, l, mid, ls, rs, k, b);
else change(p << 1 | 1, mid + 1, r, ls, rs, t[p].k, t[p].b), t[p].k = k, t[p].b = b;
}
t[p].minn = min(t[p].minn, min(l2, r2));
t[p].minn = min(t[p].minn, min(t[p << 1].minn, t[p << 1 | 1].minn)); return;
}
if(ls <= mid) change(p << 1, l, mid, ls, rs, k, b);
if(rs > mid) change(p << 1 | 1, mid + 1, r, ls, rs, k, b);
t[p].minn = min(t[p].minn, min(t[p << 1].minn, t[p << 1 | 1].minn));
}
void modify(int u, int v, ll k, ll b) {//树上跳出对应的区间
while(top[u] != top[v]) {
if(dep[top[u]] > dep[top[v]]) swap(u, v);
change(1, 1, n, dfn[top[v]], dfn[v], k, b);
v = fa[top[v]];
}
if(dep[u] > dep[v]) swap(u, v);
change(1, 1, n, dfn[u], dfn[v], k, b);
}
ll ask(int p, int l, int r, int ls, int rs) {//李超线段树的区间查询
if(ls <= l && r <= rs) return t[p].minn;
register int mid = l + r >> 1; ll res = INF;
if(t[p].b != INF) {
register int ll = max(ls, l), rr = min(rs, r);
res = min(t[p].k * dis[raw[ll]], t[p].k * dis[raw[rr]]) + t[p].b;
}
if(ls <= mid) res = min(res, ask(p << 1, l, mid, ls ,rs));
if(rs > mid) res = min(res, ask(p << 1 | 1, mid + 1, r, ls, rs));
return res;
}
ll query(int u, int v) {//树上跳区间
ll ans = INF;
while(top[u] != top[v]) {
if(dep[top[u]] > dep[top[v]]) swap(u, v);
ans = min(ans, ask(1, 1, n, dfn[top[v]], dfn[v]));
v = fa[top[v]];
}
if(dep[u] > dep[v]) swap(u, v);
return min(ans, ask(1, 1, n, dfn[u], dfn[v]));
}
signed main() {
memset(head, -1, sizeof head);
n = read(), m = read();
for(int i = 1, u, v, w; i < n; i++) u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
H.dfs1(1), H.dfs2(1, 1); build(1, 1, n);
register int op, s, t, a, b, lca;
while(m--) {
op = read(), s = read(), t = read();
if(op == 2) printf("%lld\n", query(s, t));
else a = read(), b = read(), lca = H.LCA(s, t),
modify(lca, s, -a, dis[s] * a + b), modify(lca, t, a, (dis[s] - 2 * dis[lca]) * a + b);
}
return 0;
}
迎评:)
——End——