zoukankan      html  css  js  c++  java
  • 模拟赛 T3 DFS序+树状数组+树链的并+点权/边权技巧

    题意:给定一颗树,有 $m$ 次操作.

    操作 0 :向集合 $S$ 中加入一条路径 $(p,q)$,权值为 $v$

    操作 1 :给定一个点集 $T$,求 $T$ 的并集与 $S$ 中路径含交集的权和.(就是如果路径 $i$ 与 $T$ 有交集,就产生 $v_{i}$ 的贡献)

    数据范围:路径长度 $leqslant 20$,$1leqslant n,m leqslant 10^5$

    如果路径长度为 0 (即 $S$ 中全部是点)的话我们求的就是点集 $T$ 的树链的并的权和.

    这个可以用 DFS 序 + 树状数组来维护.

    树上一个重要的性质就是任意两点之间如果经过 $i$ 个点的话会经过 $i-1$ 条边,边数总是点数-1.

    然后下一步就特别神了:

    对于新加入的一条路径:将路径上的点加上权值,边加上权值的相反数.

    你发现如果一个连通块与这条路径有并集的话必经过 $i$ 个点和 $i-1$ 条边.

    即边数恒等于点数 - 1,这就实现了只贡献一次的效果.

    对于维护一个点到根的权和,我们采用 DFS 序 + 树状数组的方式.

    #include <vector>  
    #include <cstdio> 
    #include <set> 
    #include <cstring> 
    #include <string>
    #include <algorithm> 
    #define N 200007 
    #define ll long long 
    using namespace std;   
    void setIO(string s) {
        string in=s+".in"; 
        string out=s+".out"; 
        freopen(in.c_str(),"r",stdin); 
        freopen(out.c_str(),"w",stdout);    
    }
    struct BIT {   
        ll C[N]; 
        int lowbit(int t) {
            return t&(-t); 
        }
        void update(int x,int v) {
            while(x<N) {
                C[x]+=(ll)v; 
                x+=lowbit(x); 
            }
        }
        ll query(int x) {
            ll tmp=0; 
            while(x>0) {
                tmp+=C[x]; 
                x-=lowbit(x); 
            } 
            return tmp; 
        }
    }tree;   
    int tot;    
    int n,m,L;   
    int edges; 
    int dfn; 
    int hd[N]; 
    int to[N<<1]; 
    int nex[N<<1];
    int top[N]; 
    int son[N];   
    int size[N]; 
    int dep[N];
    int A[N];  
    int fa[N];     
    int st[N];  
    int ed[N];   
    int Fa[N];     
    vector<int>G[N];         
    bool cmp(int a,int b) {
        return st[a]<st[b];   
    }
    void add(int u,int v) {
        nex[++edges]=hd[u]; 
        hd[u]=edges; 
        to[edges]=v;  
    }
    void dfs1(int u,int ff) { 
        fa[u]=ff; 
        size[u]=1;    
        dep[u]=dep[ff]+1;       
        for(int i=hd[u];i;i=nex[i]) {
            int v=to[i]; 
            if(v==ff) {
                continue;  
            }
            dfs1(v,u); 
            size[u]+=size[v];   
            if(size[v]>size[son[u]]) {
                son[u]=v; 
            }
        }   
    }
    void dfs2(int u,int tp) {  
        top[u]=tp;    
        if(son[u]) {
            dfs2(son[u],tp); 
        }
        for(int i=hd[u];i;i=nex[i]) {
            if(to[i]!=fa[u]&&to[i]!=son[u]) {
                dfs2(to[i],to[i]); 
            }
        }
    }  
    int LCA(int x,int y) {
        while(top[x]!=top[y]) {
            dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];   
        }
        return dep[x]<dep[y]?x:y;   
    }
    // 到根的权和        
    ll Sum(int x) {
        return tree.query(st[x]);        
    }        
    void build(int u) {        
        for(int i=hd[u];i;i=nex[i]) {
            int v=to[i];  
            if(v==fa[u]) {
                continue;  
            }            
            ++tot;    
            Fa[tot]=u;    
            Fa[v]=tot;        
            G[u].push_back(tot); 
            G[tot].push_back(v);           
            build(v);      
        }
    }   
    void dfs(int u) {
        st[u]=++dfn;  
        for(int i=0;i<G[u].size();++i) {
            int v=G[u][i]; 
            // printf("%d %d
    ",u,v);  
            dfs(v); 
        } 
        ed[u]=dfn;  
    }
    int main() { 
        setIO("tree"); 
        int i,j;         
        scanf("%d%d%d",&n,&m,&L);   
        for(i=1;i<n;++i) { 
            int x,y; 
            scanf("%d%d",&x,&y); 
            add(x,y); 
            add(y,x); 
        }
        dfs1(1,0); 
        dfs2(1,1);               
        tot=n;      
        build(1); 
        dfs(1);  
        while(m--) {
            int op; 
            scanf("%d",&op);  
            if(op==0) {               
                int p,q,v,d=1; 
                scanf("%d%d%d",&p,&q,&v);   
                int lca=LCA(p,q);  
                while(p!=lca) {                 
                    tree.update(st[p],d*v);         
                    tree.update(ed[p]+1,-d*v);   
                    d*=-1;   
                    p=Fa[p];  
                } 
                d=1; 
                while(q!=lca) {
                    tree.update(st[q],d*v); 
                    tree.update(ed[q]+1,d*v); 
                    d*=-1;   
                    q=Fa[q];          
                }
                tree.update(st[lca],v);    
                tree.update(ed[lca]+1,-v); 
            }                         
            else {   
                int a,cnt=0; 
                scanf("%d",&a);  
                for(i=1;i<=a;++i) {
                    scanf("%d",&A[++cnt]); 
                }
                scanf("%d",&a); 
                for(i=1;i<=a;++i) {
                    scanf("%d",&A[++cnt]); 
                }         
                sort(A+1,A+1+cnt,cmp);   
                int lca=A[1];  
                for(i=2;i<=cnt;++i) {
                    lca=LCA(lca,A[i]); 
                }            
                ll ans=0; 
                for(i=1;i<=cnt;++i) {   
                    ans+=Sum(A[i])-Sum(Fa[lca]);     
                }
                for(i=2;i<=cnt;++i) {
                    ans-=Sum(LCA(A[i],A[i-1]))-Sum(Fa[lca]);   
                }        
                printf("%lld
    ",ans);  
            }
        }
        return 0; 
    }
    

      

  • 相关阅读:
    使用golang访问kubebernetes
    使用 Rancher 管理现有 Kubernetes 集群
    Running powershell scripts during nuget package installation and removal
    How to Create, Use, and Debug .NET application Crash Dumps in 2019
    寻找写代码感觉(一)之使用 Spring Boot 快速搭建项目
    Selenium+Java之解决org.openqa.selenium.InvalidArgumentException: invalid argument报错问题
    Selenium环境搭建
    关于Xpath定位方法知道这些基本够用
    Web自动化之浏览器启动
    【翻译】编写代码注释的最佳实践
  • 原文地址:https://www.cnblogs.com/guangheli/p/12074444.html
Copyright © 2011-2022 走看看