Luogu3787 冰精凍西瓜(樹上操作)題解
阿新 • • 發佈:2020-11-03
這題面不禁讓我聯想到了樹剖
思路
顯然,這道題並不需要樹剖畢竟它只是藍題,不過,它也需要用到\(DFS\)序,把樹上操作轉化成序列操作(這個思想可以說很套路了),但這題有一個麻煩的地方:在修改子樹時每一條邊都會對修改值造成影響,而這用線段樹是難以維護的。
於是我們考慮將邊上的影響分離出來,可以先預處理出一個從當前節點到根的字首積,在每次子樹加時將加數除以當前節點的字首積,查詢時再乘上查詢節點的字首積,我們發現,通過這樣的操作,就將修改節點到根節點的字首積乘回來了。是不是很妙
But
還有一個細節:資料範圍中特意強調了\(w_i\)可能為\(0\),而我們知道,當\(w_i\)為0時,修改其他節點(指除i及其子樹以外的節點)就對i節點及其子樹沒有影響了。為了方便地處理,我們可以巧妙地將這些\(w_i\)
程式碼
#include <cstdio> #include <algorithm> #include <cmath> using namespace std; const int maxn = 2e5 + 10; //兩倍空間開了嗎 int n,head[maxn],num,m; int rm[maxn],rt[maxn],top; double tim[maxn]; struct Edge{ int then,to; double val; }e[maxn]; void add(int u, int v, double val){e[++num] = (Edge){head[u], v, val}; head[u] = num;} int dfn[maxn],id[maxn],cnt,siz[maxn]; void DFS(int u, int f){ dfn[++cnt] = u, id[u] = cnt; siz[u] = 1; for(int i = head[u]; i; i = e[i].then){ int v = e[i].to; if(v != f && !rm[v] && !id[v]){ if(fabs(e[i].val - 0.0) <= 0.00000001) rm[v] = 1, rt[++top] = v, tim[v] = 1; //注意double判0的時候儘量別用== else tim[v] = tim[u] * e[i].val, DFS(v, u), siz[u] += siz[v]; } } } struct Seg_Tree{ #define lc(x) x << 1 #define rc(x) x << 1 | 1 double c[maxn << 2],tag[maxn << 2]; void f(int l, int r, int p, double x){ c[p] += (r - l + 1) * x; tag[p] += x; } void downdate(int l, int r, int p){ if(fabs(tag[p] - 0.0) >= 0.00000001){ int mid = (l + r) >> 1; f(l, mid, lc(p), tag[p]); f(mid + 1, r, rc(p), tag[p]); tag[p] = 0; } } void add(int L, int R, int l, int r, int p, double val){ if(L <= l && R >= r){ f(l, r, p, val); return; } downdate(l, r, p); int mid = (l + r) >> 1; if(L <= mid) add(L, R, l, mid, lc(p), val); if(mid < R) add(L, R, mid + 1, r, rc(p), val); c[p] = c[lc(p)] + c[rc(p)]; } double query(int pos, int l, int r, int p){ if(l == r) return c[p]; downdate(l, r, p); int mid = (l + r) >> 1; if(mid >= pos) return query(pos, l, mid, lc(p)); else return query(pos, mid + 1, r, rc(p)); } }tree; void push(int u, double val){ val /= tim[u]; tree.add(id[u], id[u] + siz[u] - 1, 1, cnt, 1, val); } double get_ans(int u){ double ans = tree.query(id[u], 1, cnt, 1); return ans * tim[u]; } int main(){ scanf("%d", &n); for(int i = 1; i < n; ++ i){ int u,v; double val; scanf("%d%d%lf", &u, &v, &val); add(u, v, val), add(v, u, val); } tim[1] = 1, DFS(1, 0); for(int i = 1; i <= top; ++ i) DFS(rt[i], 0); scanf("%d", &m); while(m--){ int opt,Id; double x; scanf("%d%d", &opt, &Id); if(opt == 1){ scanf("%lf", &x); push(Id, x); } else printf("%.8lf\n", get_ans(Id)); } return 0; }