【bzoj3589】动态树【树链剖分】【线段树】

题目链接
题解
首先,第一个操作就是子树加,直接线段树对应dfn序区间加即可。
第二个操作,我写了个奇怪的东西,单次查询大概是两个log的,常数大上天……其实,这个过程类似于线段覆盖。我们可以维护
setv:该区间是否被完全覆盖
tot:该区间被覆盖的个数。
然后我们在线段树上,对着没覆盖完的节点走一遍。先找到查询的区间,然后遇到完全覆盖的节点和叶子节点返回,完全未覆盖的区间更新答案和标记再返回,否则往左右儿子走。最后把变了的节点复原。细节见代码。
然后就A了。
代码

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=200005;
int n,m,op,k,u,v,cnt,ans,head[N],to[N*2],nxt[N*2];
int idx,fa[N],dep[N],siz[N],son[N],dfn[N],pos[N],top[N];
int sumv[N*4],setv[N*4],tot[N*4],tag1[N*4],tag2[N*4],fix[N*8];
void adde(int u,int v){
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
void dfs(int u){
    siz[u]=1;
    int v;
    for(int i=head[u];i;i=nxt[i]){
        v=to[i];
        if(v!=fa[u]){
            fa[v]=u;
            dep[v]=dep[u]+1;
            dfs(v);
            siz[u]+=siz[v];
            if(!son[u]||siz[son[u]]<siz[v]){
                son[u]=v;
            }
        }
    }
}
void dfs(int u,int tp){
    dfn[u]=++idx;
    pos[idx]=u;
    top[u]=tp;
    if(son[u]){
        dfs(son[u],tp);
    }
    int v;
    for(int i=head[u];i;i=nxt[i]){
        v=to[i];
        if(v!=fa[u]&&v!=son[u]){
            dfs(v,v);
        }
    }
}
void pushdown(int o,int l,int r){
    int mid=(l+r)/2;
    if(tag1[o]){
        tag1[o*2]+=tag1[o];
        tag1[o*2+1]+=tag1[o];
        sumv[o*2]+=tag1[o]*(mid-l+1);
        sumv[o*2+1]+=tag1[o]*(r-mid);
        tag1[o]=0;
    }
    if(tag2[o]){
        tag2[o*2]=tag2[o*2+1]=setv[o*2]=setv[o*2+1]=1;
        tot[o*2]=mid-l+1;
        tot[o*2+1]=r-mid;
        fix[++fix[0]]=o*2;
        fix[++fix[0]]=o*2+1;
        tag2[o]=0;
    }
}
void update(int o,int l,int r,int L,int R,int v){
    if(L<=l&&R>=r){
        sumv[o]+=v*(r-l+1);
        tag1[o]+=v;
        return;
    }
    pushdown(o,l,r);
    int mid=(l+r)/2;
    if(L<=mid){
        update(o*2,l,mid,L,R,v);
    }
    if(R>mid){
        update(o*2+1,mid+1,r,L,R,v);
    }
    sumv[o]=sumv[o*2]+sumv[o*2+1];
}
void query(int o,int l,int r,int L,int R){
    fix[++fix[0]]=o;
    if(L<=l&&R>=r){
        if(!tot[o]){
            ans+=sumv[o];
            setv[o]=tag2[o]=1;
            tot[o]=r-l+1;
            fix[++fix[0]]=o;
            return;
        }
        if(l==r||setv[o]){
            return;
        }
        pushdown(o,l,r);
        int mid=(l+r)/2;
        if(tot[o*2]<mid-l+1){
            query(o*2,l,mid,L,R);
        }
        if(tot[o*2+1]<r-mid){
            query(o*2+1,mid+1,r,L,R);
        }
        setv[o]=setv[o*2]&&setv[o*2+1];
        tot[o]=tot[o*2]+tot[o*2+1];
        return;
    }
    pushdown(o,l,r);
    int mid=(l+r)/2;
    if(L<=mid){
        query(o*2,l,mid,L,R);
    }
    if(R>mid){
        query(o*2+1,mid+1,r,L,R);
    }
    setv[o]=setv[o*2]&&setv[o*2+1];
    tot[o]=tot[o*2]+tot[o*2+1];
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        adde(u,v);
        adde(v,u);
    }
    dfs(1);
    dfs(1,1);
    scanf("%d",&m);
    while(m--){
        scanf("%d",&op);
        if(!op){
            scanf("%d%d",&u,&v);
            update(1,1,n,dfn[u],dfn[u]+siz[u]-1,v);
        }else{
            scanf("%d",&k);
            ans=0;
            while(k--){
                scanf("%d%d",&u,&v);
                while(top[u]!=top[v]){
                    if(dep[top[u]]<dep[top[v]]){
                        swap(u,v);
                    }
                    query(1,1,n,dfn[top[u]],dfn[u]);
                    u=fa[top[u]];
                }
                if(dep[u]>dep[v]){
                    swap(u,v);
                }
                query(1,1,n,dfn[u],dfn[v]);
            }
            printf("%d\n",ans&2147483647);
            for(int i=1;i<=fix[0];i++){
                setv[fix[i]]=tag2[fix[i]]=tot[fix[i]]=0;
            }
            fix[0]=0;
        }
    }
    return 0;
}
posted @ 2018-07-04 21:01  一剑霜寒十四洲  阅读(132)  评论(0编辑  收藏  举报