bzoj 4515. [Sdoi2016]遊戲(樹鏈剖分 + 李超線段樹(真·模板) + 李超樹維護區間最小值)

洛谷鏈接

在這裏插入圖片描述


樹剖一下,直觀上來看,是要在樹上對一條鏈維護一段等差數列。如果維護的是區間和,每次在一段區間加上一段等差數列,這個可以直接在線段樹上做不依賴任何科技,但這題的查詢形式是最小值,直接做很難打標記進行維護。

將等差數列視作一次函數,考慮用李超樹在鏈上維護一個一次函數。
預處理出每個點的深度值 dep[u]
在路徑 s,ts,t 上加入一條 斜率爲 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]))

這題還有一個特殊的地方,就是一次函數放到了樹上,進行樹剖建線段樹後,線段樹的下標不是 xx 而是 dfsdfs 序,連續的一段 dfsdfs 序不一定是一條鏈,而橫座標(深度值)僅在一條鏈上滿足單調性,在整棵樹上不滿足單調性,維護的線段的區間在維護過程中不能有誤。

線段樹上的每個節點的區間被該維護的線段的區間完全覆蓋,這樣在樹上不會存在一條跨鏈的一次函數,在 pushup 不容易出問題。

李超線段樹的複雜度是 nlog2nn\log^2 n,樹剖還有一個 log\log,複雜度爲 nlog3nn \log^3 n,但樹剖和李超樹的常數都非常小,可能在 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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章