Luogu3787 冰精冻西瓜(树上操作)题解
这题面不禁让我联想到了树剖
思路
显然,这道题并不需要树剖毕竟它只是蓝题,不过,它也需要用到\(DFS\)序,把树上操作转化成序列操作(这个思想可以说很套路了),但这题有一个麻烦的地方:在修改子树时每一条边都会对修改值造成影响,而这用线段树是难以维护的。
于是我们考虑将边上的影响分离出来,可以先预处理出一个从当前节点到根的前缀积,在每次子树加时将加数除以当前节点的前缀积,查询时再乘上查询节点的前缀积,我们发现,通过这样的操作,就将修改节点到根节点的前缀积乘回来了。是不是很妙
But
还有一个细节:数据范围中特意强调了\(w_i\)可能为\(0\),而我们知道,当\(w_i\)为0时,修改其他节点(指除i及其子树以外的节点)就对i节点及其子树没有影响了。为了方便地处理,我们可以巧妙地将这些\(w_i\)为\(0\)的点也设为根,将原树拆分成几棵树遍历,这样就可以解决\(0\)的问题且子树编号仍然连续了。同时,我们每一个点仍然只会遍历一次(为根节点两次),所以遍历仍然是\(O(n)\)级别的。
代码
#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;
}