zoukankan      html  css  js  c++  java
  • 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
    ", get_ans(Id));
        }
        return 0;
    }
    
  • 相关阅读:
    __iter__方法demo
    开放封闭原则
    单例模式
    Python赋值、浅拷贝、深拷贝
    保留原页面的参数条件
    request.GET、request.POST、request.body(持续更新)
    面向对象的封装、继承、多态(持续更新)
    关于Form、ModelForm的一些操作(持续更新)
    创建类的两种方式
    Nginx深度优化
  • 原文地址:https://www.cnblogs.com/whenc/p/13922229.html
Copyright © 2011-2022 走看看