題目描述
毛毛蟲經過及時的變形,最終逃過的一劫,離開了菜媽的菜園。 毛毛蟲經過千山萬水,歷盡千辛萬苦,最後來到了小小的紹興一中的校園裏。
爬啊爬~爬啊爬毛毛蟲爬到了一顆小小的“毛景樹”下面,發現樹上長着他最愛喫的毛毛果~ “毛景樹”上有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記得改宏定義!!!
}