• 模拟赛 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; 
    }
    

      

  • 相关阅读:
    2014年5月16日
    2014年4月8日
    Qt 小技巧之“To-Do 事项”
    koa中间件实现分析
    关于计算透视投影的四条边的方法,留作备忘
    关于向量
    关于ngui协同
    关于NGUI分辨率
    动态修改NGUI UI2DSprite
    动态设置viewport的宽高
  • 原文地址:https://www.cnblogs.com/guangheli/p/12074444.html
Copyright © 2020-2023  润新知