洛谷P4315 月下“毛景樹” (樹鏈剖分&線段樹)

題目描述

毛毛蟲經過及時的變形,最終逃過的一劫,離開了菜媽的菜園。 毛毛蟲經過千山萬水,歷盡千辛萬苦,最後來到了小小的紹興一中的校園裏。

爬啊爬~爬啊爬毛毛蟲爬到了一顆小小的“毛景樹”下面,發現樹上長着他最愛喫的毛毛果~ “毛景樹”上有N個節點和N-1條樹枝,但節點上是沒有毛毛果的,毛毛果都是長在樹枝上的。但是這棵“毛景樹”有着神奇的魔力,他能改變樹枝上毛毛果的個數:

  • Change k w:將第k條樹枝上毛毛果的個數改變爲w個。

  • Cover u v w:將節點u與節點v之間的樹枝上毛毛果的個數都改變爲w個。

  • Add u v w:將節點u與節點v之間的樹枝上毛毛果的個數都增加w個。 由於毛毛蟲很貪,於是他會有如下詢問:

  • Max u v:詢問節點u與節點v之間樹枝上毛毛果個數最多有多少個。

輸入格式

第一行一個正整數N。

接下來N-1行,每行三個正整數Ui,Vi和Wi,第i+1行描述第i條樹枝。表示第i條樹枝連接節點Ui和節點Vi,樹枝上有Wi個毛毛果。 接下來是操作和詢問,以“Stop”結束。

輸出格式

對於毛毛蟲的每個詢問操作,輸出一個答案。

輸入輸出樣例

輸入 #1複製

4
1 2 8
1 3 7
3 4 9
Max 2 4
Cover 2 4 5
Add 1 4 10
Change 1 16
Max 2 4
Stop

輸出 #1複製

9
16

說明/提示

1<=N<=100,000,操作+詢問數目不超過100,000。

保證在任意時刻,所有樹枝上毛毛果的個數都不會超過10^9個。

 

原來區間覆蓋可以直接標記的,第一次寫的時候乘0再加,寫的麻煩又難調QAQ

解法

除去樹剖部分,就是線段樹的區間覆蓋,區間加,區間最大值,區間覆蓋的優先級是大於區間加的,Pushdown的過程中先更新覆蓋部分,並且把加法的lazy置爲0。邊權可以用深度較大的點權來表示,所以更新同一條重鏈的時候,左區間需要+1。這些是我寫的過程中遇到的問題,剩下就是改板子啦。

Accepted code

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair <int, int>
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 1e5 + 100;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % Mod; b >>= 1; t = (t*t) % Mod; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }

// 存圖
vector <pir> G[N << 1];
int fz[N], son[N], sz[N];
int idx[N], dfn;
int dep[N], top[N];
int wt[N], ev[N], n, q;
pir a[N];
char op[10];

void dfs1(int x, int fa, int dist) {
	fz[x] = fa;     // 父親
	dep[x] = dist;  //深度
	sz[x] = 1;      // 子樹大小

	int mx = 0;   // 重兒子個數
	for (auto it : G[x]) {
		int v = it.second;
		int w = it.first;
		if (v == fa)
			continue;
		wt[v] = w;
		dfs1(v, x, dist + 1);
		sz[x] += sz[v];
		if (sz[v] > mx)
			mx = sz[v], son[x] = v;
	}
}
void dfs2(int x, int topfz) {
	idx[x] = ++dfn;      // 重新編號
	top[x] = topfz;
	ev[dfn] = wt[x];

	if (!son[x])
		return;
	dfs2(son[x], topfz);

	for (auto it : G[x]) {
		int v = it.second;
		if (v != fz[x] && v != son[x])
			dfs2(v, v);
	}
}

// 線段樹
int mx[N * 4];
int lzy[N * 4], add[N * 4]; // 覆蓋,加

void Build(int rt, int L, int R) {
	lzy[rt] = -1;
	if (L == R) {
		mx[rt] = ev[L];   // 這個映射關係搞清楚,是重新編號後的點
		return;
	}
	int mid = (L + R) >> 1;
	Build(ls, L, mid);
	Build(rs, mid + 1, R);
	mx[rt] = max(mx[ls], mx[rs]);
}
void Pushdown(int rt) {    
	if (lzy[rt] != -1) {    // 優先覆蓋
		add[ls] = add[rs] = 0;       // 注意清0哦
		mx[ls] = mx[rs] = lzy[rt];
		lzy[ls] = lzy[rs] = lzy[rt];

		lzy[rt] = -1;
	}
	if (add[rt]) {          // 區間加
		mx[ls] += add[rt], mx[rs] += add[rt];
		add[ls] += add[rt], add[rs] += add[rt];

		add[rt] = 0;
	}
}
void Update(int rt, int L, int R, int l, int r, int w) {  // 區間覆蓋
	if (L >= l && R <= r) {
		mx[rt] = w;
		lzy[rt] = w, add[rt] = 0;
		return;
	}
	Pushdown(rt);
	int mid = (L + R) >> 1;
	if (mid >= l)
		Update(ls, L, mid, l, r, w);
	if (mid < r)
		Update(rs, mid + 1, R, l, r, w);
	mx[rt] = max(mx[ls], mx[rs]);
}
void Add(int rt, int L, int R, int l, int r, int w) {   // 區間加
	if (L >= l && R <= r) {
		mx[rt] += w;
		add[rt] += w;
		return;
	}
	Pushdown(rt);
	int mid = (L + R) >> 1;
	if (mid >= l)
		Add(ls, L, mid, l, r, w);
	if (mid < r)
		Add(rs, mid + 1, R, l, r, w);
	mx[rt] = max(mx[ls], mx[rs]);
}
int Query(int rt, int L, int R, int l, int r) {    // 區間查詢
	if (L >= l && R <= r)
		return mx[rt];
	Pushdown(rt);
	int mid = (L + R) >> 1;
	int tot = 0;
	if (mid >= l)
		Max(tot, Query(ls, L, mid, l, r));
	if (mid < r)
		Max(tot, Query(rs, mid + 1, R, l, r));
	return tot;
}
void Range_Update(int x, int y, int w) {    
	while (top[x] != top[y]) {       // 先輕鏈
		if (dep[top[x]] < dep[top[y]])
			swap(x, y);
		Update(1, 1, n, idx[top[x]], idx[x], w);
		x = fz[top[x]];
	}
	if (dep[x] > dep[y])          // 後重鏈
		swap(x, y);
	Update(1, 1, n, idx[x] + 1, idx[y], w);
}
void Range_Add(int x, int y, int w) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]])
			swap(x, y);
		Add(1, 1, n, idx[top[x]], idx[x], w);
		x = fz[top[x]];
	}
	if (dep[x] > dep[y])
		swap(x, y);
	Add(1, 1, n, idx[x] + 1, idx[y], w);
}
int Range_Query(int x, int y) {         // 都一樣啦
	int tot = 0;
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]])
			swap(x, y);
		Max(tot, Query(1, 1, n, idx[top[x]], idx[x]));
		x = fz[top[x]];
	}
	if (dep[x] > dep[y])
		swap(x, y);
	Max(tot, Query(1, 1, n, idx[x] + 1, idx[y]));
	return tot;
}

int main()
{
	cin >> n;
	for (int i = 1; i < n; i++) {
		int u, v, w;
		sc("%d %d %d", &u, &v, &w);
		G[u].push_back({ w, v });
		G[v].push_back({ w, u });
		a[i] = { u, v };          // 保存邊的編號
	} 
	dfs1(1, 0, 1);
	dfs2(1, 1);
	Build(1, 1, n);    // 樹剖+建樹

	while (sc("%s", op) && op[0] != 'S') {
		int x, y, w;
		if (op[1] == 'h') {
			sc("%d %d", &x, &w);
			int u = a[x].first;
			int v = a[x].second;
			if (dep[u] < dep[v])    // 度數較大的點來表示邊
				swap(u, v);
			Update(1, 1, n, idx[u], idx[u], w);
		}
		else if (op[1] == 'o') {   // 區間覆蓋
			sc("%d %d %d", &x, &y, &w);
			Range_Update(x, y, w);
		}
		else if (op[0] == 'A') {   // 區間加
			sc("%d %d %d", &x, &y, &w);
			Range_Add(x, y, w);
		}
		else {                    // 區間查詢
			sc("%d %d", &x, &y);
			printf("%d\n", Range_Query(x, y));
		}
	}
	return 0;  // 改數組大小!!!用pair記得改宏定義!!!
}

 

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