模拟赛 T3 dfs序+树状数组+树链的并+点权/边权技巧

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

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

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

数据范围:路径长度 $\leqslant 20$,$1\leqslant 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\n",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\n",ans);  
        }
    }
    return 0; 
}

  

猜你喜欢

转载自www.cnblogs.com/guangheli/p/12074444.html
今日推荐