樹剖一下,直觀上來看,是要在樹上對一條鏈維護一段等差數列。如果維護的是區間和,每次在一段區間加上一段等差數列,這個可以直接在線段樹上做不依賴任何科技,但這題的查詢形式是最小值,直接做很難打標記進行維護。
將等差數列視作一次函數,考慮用李超樹在鏈上維護一個一次函數。
預處理出每個點的深度值 dep[u]
在路徑 上加入一條 斜率爲 a,截距爲 b
的直線,在 s,lca(s,t)
路徑上點x
加入的數字顯然是 b + a * (dep[s] - dep[x]) = -a * dep[x] + b + a * dep[s]
,用李超樹將這條線段維護到樹鏈上,路徑的另外半段同樣的分析方法。
由於要維護的是最小值,在李超樹上每個節點要維護中點 mid
處最低的線段,這個直接改板子。由於詢問形式去區間詢問,考慮再維護一個區間最小值 val[rt]
,在加入一條線段之後每個節點的最小值 = min(當前區間最低勢線段兩端的取值,min(val[ls],val[rs]))
這題還有一個特殊的地方,就是一次函數放到了樹上,進行樹剖建線段樹後,線段樹的下標不是 而是 序,連續的一段 序不一定是一條鏈,而橫座標(深度值)僅在一條鏈上滿足單調性,在整棵樹上不滿足單調性,維護的線段的區間在維護過程中不能有誤。
線段樹上的每個節點的區間被該維護的線段的區間完全覆蓋,這樣在樹上不會存在一條跨鏈的一次函數,在 pushup 不容易出問題。
李超線段樹的複雜度是 ,樹剖還有一個 ,複雜度爲 ,但樹剖和李超樹的常數都非常小,可能在 1s 內通過
代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int N = 1e5;
typedef long long ll;
ll inf = 123456789123456789ll;
int n,m,q,op,s,t;
ll a,b;
struct Line { //直線結構體
ll k,b;
Line() {}
Line(ll ki,ll bi) {
k = ki, b = bi;
}
ll calc(ll x) { //計算在 x 點的 y值
return k * x + b;
}
};
struct Graph {
int head[maxn],nxt[maxn << 1],to[maxn << 1],w[maxn << 1];
int cnt;
void init() {
memset(head,-1,sizeof head);
cnt = 0;
}
void add(int u,int v,int wi) {
to[cnt] = v;
w[cnt] = wi;
nxt[cnt] = head[u];
head[u] = cnt++;
}
}G;
int dfn[maxn],dis[maxn],son[maxn],idfn[maxn],sz[maxn],top[maxn],f[maxn],cnt;
ll dep[maxn];
void dfs1(int u,int fa) { //預處理
sz[u] = 1, f[u] = fa; son[u] = 0; dis[u] = dis[fa] + 1;
for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
int v = G.to[i], w = G.w[i];
if (v == fa) continue;
dep[v] = dep[u] + w;
dfs1(v,u);
sz[u] += sz[v];
if (!son[u] || sz[v] > sz[son[u]])
son[u] = v;
}
}
void dfs2(int u,int t) { //輕重鏈剖分
dfn[u] = ++cnt, idfn[cnt] = u;
top[u] = t;
if (!son[u]) return ;
dfs2(son[u],t);
for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
int v = G.to[i];
if (v == f[u] || v == son[u]) continue;
dfs2(v,v);
}
}
struct seg_tree { //維護 x = k 處最低線段
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
int tag[maxn << 2];
ll val[maxn << 2]; //val 維護區間的最小值
Line line[maxn << 2];
void build(int rt,int l,int r) {
line[rt].k = 0; line[rt].b = inf;
val[rt] = inf;
if (l == r) return;
int mid = l + r >> 1;
build(lson); build(rson);
}
void update(int rt,int l,int r,int L,int R,Line t) {
if (L <= l && r <= R) {
int mid = l + r >> 1;
if (line[rt].calc(dep[idfn[l]]) > t.calc(dep[idfn[l]]) && line[rt].calc(dep[idfn[r]]) > t.calc(dep[idfn[r]])) {
line[rt] = t;
} else if (line[rt].calc(dep[idfn[l]]) > t.calc(dep[idfn[l]]) || line[rt].calc(dep[idfn[r]]) > t.calc(dep[idfn[r]])) {
if (line[rt].calc(dep[idfn[mid]]) > t.calc(dep[idfn[mid]])) {
Line tmp = t; t = line[rt]; line[rt] = tmp;
}
if (t.k > line[rt].k) {
update(lson,L,R,t);
} else {
update(rson,L,R,t);
}
}
val[rt] = min(val[rt],min(line[rt].calc(dep[idfn[l]]),line[rt].calc(dep[idfn[r]])));
if (l != r) val[rt] = min(val[rt],min(val[rt << 1],val[rt << 1 | 1]));
} else {
int mid = l + r >> 1;
if (L <= mid) update(lson,L,R,t);
if (mid + 1 <= R) update(rson,L,R,t);
if (l != r) val[rt] = min(val[rt],min(val[rt << 1],val[rt << 1 | 1]));
}
}
ll query(int L,int R,int rt,int l,int r) { //查詢區間 L,R 最小值
if (L <= l && r <= R) return val[rt];
ll ans = inf;
ans = min(ans,min(line[rt].calc(dep[idfn[max(L,l)]]),line[rt].calc(dep[idfn[min(R,r)]])));
int mid = l + r >> 1;
if (L <= mid) ans = min(ans,query(L,R,lson));
if (mid + 1 <= R) ans = min(ans,query(L,R,rson));
return ans;
}
}seg;
int getlca(int x,int y) {
while (top[x] != top[y]) {
if (dis[top[x]] < dis[top[y]]) swap(x,y);
x = f[top[x]];
}
if (dis[x] > dis[y]) swap(x,y);
return x;
}
void update(int u,int v,ll a,ll b) {
while (top[u] != top[v]) {
if (dis[top[u]] < dis[top[v]]) swap(u,v);
seg.update(1,1,n,dfn[top[u]],dfn[u],Line(a,b));
u = f[top[u]];
}
if (dis[u] > dis[v]) swap(u,v);
seg.update(1,1,n,dfn[u],dfn[v],Line(a,b));
}
ll qry(int u,int v) {
ll ans = inf;
while (top[u] != top[v]) {
if (dis[top[u]] < dis[top[v]]) swap(u,v);
ans = min(ans,seg.query(dfn[top[u]],dfn[u],1,1,n));
u = f[top[u]];
}
if (dis[u] > dis[v]) swap(u,v);
ans = min(ans,seg.query(dfn[u],dfn[v],1,1,n));
return ans;
}
int main() {
G.init();
scanf("%d%d",&n,&m);
for (int i = 1; i < n; i++) {
int u,v,w; scanf("%d%d%d",&u,&v,&w);
G.add(u,v,w);
G.add(v,u,w);
}
dfs1(1,0); dfs2(1,1);
seg.build(1,1,n);
while (m--) {
scanf("%d%d%d",&op,&s,&t);
if (op == 1) {
scanf("%lld%lld",&a,&b);
int lca = getlca(s,t);
update(s,lca,-a,b + a * dep[s]);
update(t,lca,a,b + a * dep[s] - 2 * a * dep[lca]);
} else {
printf("%lld\n",qry(s,t));
}
}
return 0;
}