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;
}
posted @ 2020-11-03 20:08  When_C  阅读(71)  评论(0编辑  收藏  举报