首页 > 其他分享 >黑白树 题解

黑白树 题解

时间:2023-01-29 15:45:54浏览次数:40  
标签:rt val int 题解 黑白 tree valb valw

看了半天题解代码大概明白他在干啥了。写篇题解总结一下。

题面:一棵树,每个点黑色或白色,有权值。五个操作:

  1. 改变一个点颜色。
  2. 使点 \(x\) 所在的同色连通块权值加 \(val\)。
  3. 查询 \(x\) 同色连通块最大值。
  4. 链 \((x,y)\) 权值加 \(val\)。
  5. \(x\) 子树加 \(val\)。

首先我们看到 \(4,5\) 两个操作知道这是个树剖。然后我们得考虑怎么在树剖的一棵线段树上把颜色一起维护上。

树剖以后连通块就变成了一堆链,也就是一大堆连续段。显然不可能一个一个修改,原地爆炸。那么考虑在下传的时候搞点事情。

具体的,我们可以把每个节点的子树在 dfs 序上代表的区间在线段树上插入,来标明这个区间是什么颜色的。然后进行和颜色有关的操作的时候就看一下这个区间是什么颜色,如果颜色不同直接返回。这个区间可以每个线段树节点上个 set 维护。

这样就相对比较好搞了,来看前三个操作:

改变颜色,把这个节点代表的区间删除,反转颜色和信息,然后再把区间加进去。

连通块加,找到连通块的根节点(就是最上边的那个节点,这个节点的子树能覆盖整个连通块),然后直接更新,注意颜色。

查询连通块,和连通块加差不多。

思路只要捋明白其实很显明,但是码量巨大。

(实际上我觉得还不如直接看代码更好理解)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int inf=2147483647;
int n,m;
struct node{
    int v,next;
}edge[400010];
int t,head[200010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int num,dfn[200010],rk[200010],fa[200010],dep[200010],size[200010],son[200010],Fa[200010][21];
int L[200010],R[200010],col[200010],val[200010];
//树剖
void dfs1(int x,int f){
    dep[x]=dep[f]+1;size[x]=1;fa[x]=Fa[x][0]=f;
    for(int i=1;i<=__lg(n);i++)Fa[x][i]=Fa[Fa[x][i-1]][i-1];
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            dfs1(edge[i].v,x);
            size[x]+=size[edge[i].v];
            if(size[edge[i].v]>size[son[x]])son[x]=edge[i].v;
        }
    }
}
void dfs2(int x,int f,int tp){
    dfn[x]=++num;L[x]=tp;rk[num]=x;
    if(son[x])dfs2(son[x],x,tp);
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=son[x]&&edge[i].v!=f)dfs2(edge[i].v,x,edge[i].v);
    }
    R[x]=num;
}
//线段树
struct tr{
    set<int>b,w;//黑白区间
    int valw,valb,lz,lzb,lzw;//分别存储黑色和白色的信息
    bool covw,covb;//区间所有点是否全都是黑色/白色
}tree[800010];
void pushtag(int rt,int val){
    tree[rt].valw+=val;tree[rt].valb+=val;tree[rt].lz+=val;
}
void pushup(int rt){
    tree[rt].valw=tree[rt].valb=-inf;
    if(tree[lson].b.empty())tree[rt].valw=max(tree[rt].valw,tree[lson].valw);
    if(tree[rson].b.empty())tree[rt].valw=max(tree[rt].valw,tree[rson].valw);
    if(tree[lson].w.empty())tree[rt].valb=max(tree[rt].valb,tree[lson].valb);
    if(tree[rson].w.empty())tree[rt].valb=max(tree[rt].valb,tree[rson].valb);
    tree[rt].covb=tree[lson].covb&&tree[rson].covb;
    tree[rt].covw=tree[lson].covw&&tree[rson].covw;
}
//检查tree[rt]的线段树区间是否有颜色col
bool check(int rt,int l,int r,int col){
    set<int>::iterator it=(col?tree[rt].b:tree[rt].w).lower_bound(l);
    if(it==(col?tree[rt].b:tree[rt].w).end())return false;
    return (*it)<=r;
}
void pushdown(int rt){
    if(tree[rt].lz){
        pushtag(lson,tree[rt].lz);pushtag(rson,tree[rt].lz);
        tree[rt].lz=0;
    }
    if(tree[rt].lzb){
        if(tree[lson].w.empty()){
            tree[lson].valb+=tree[rt].lzb;tree[lson].lzb+=tree[rt].lzb;
        }
        if(tree[rson].w.empty()){
            tree[rson].valb+=tree[rt].lzb;tree[rson].lzb+=tree[rt].lzb;
        }
        tree[rt].lzb=0;
    }
    if(tree[rt].lzw){
        if(tree[lson].b.empty()){
            tree[lson].valw+=tree[rt].lzw;tree[lson].lzw+=tree[rt].lzw;
        }
        if(tree[rson].b.empty()){
            tree[rson].valw+=tree[rt].lzw;tree[rson].lzw+=tree[rt].lzw;
        }
        tree[rt].lzw=0;
    }
}
void build(int rt,int l,int r){
    if(l==r){
        if(col[rk[l]]){
            tree[rt].valb=val[rk[l]];tree[rt].valw=-inf;tree[rt].covb=true;
        }
        else{
            tree[rt].valw=val[rk[l]];tree[rt].valb=-inf;tree[rt].covw=true;
        }
        return;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);build(rson,mid+1,r);
    pushup(rt);
}
//反转颜色信息
void update(int rt,int l,int r,int pos){
    if(l==r){
        swap(tree[rt].valb,tree[rt].valw);
        swap(tree[rt].covb,tree[rt].covw);
        return;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(pos<=mid)update(lson,l,mid,pos);
    else update(rson,mid+1,r,pos);
    pushup(rt);
}
//插入删除区间
void ins(int rt,int L,int R,int l,int r,int k,int p){
    if(l<=L&&R<=r){
        (p?tree[rt].b:tree[rt].w).insert(k);return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)ins(lson,L,mid,l,r,k,p);
    if(mid<r)ins(rson,mid+1,R,l,r,k,p);
    pushup(rt);
}
void del(int rt,int L,int R,int l,int r,int k,int p){
    if(l<=L&&R<=r){
        (p?tree[rt].b:tree[rt].w).erase(k);return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)del(lson,L,mid,l,r,k,p);
    if(mid<r)del(rson,mid+1,R,l,r,k,p);
    pushup(rt);
}
//45操作的update
void modi(int rt,int L,int R,int l,int r,int w){
    if(l<=L&&R<=r){
        pushtag(rt,w);return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)modi(lson,L,mid,l,r,w);
    if(mid<r)modi(rson,mid+1,R,l,r,w);
    pushup(rt);
}
//修改某个颜色块
void Modi(int rt,int L,int R,int l,int r,int w,int p){
    if(check(rt,l,r,p^1))return;//如果当前段有相反颜色则直接返回
    if(l<=L&&R<=r){
        if(p)tree[rt].valb+=w,tree[rt].lzb+=w;
        else tree[rt].valw+=w,tree[rt].lzw+=w;
        return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)Modi(lson,L,mid,l,r,w,p);
    if(mid<r)Modi(rson,mid+1,R,l,r,w,p);
    pushup(rt);
}
//查询颜色块 和上边差不多
int query(int rt,int L,int R,int l,int r,int p){
    if(check(rt,l,r,p^1))return 0;
    if(l<=L&&R<=r)return p?tree[rt].valb:tree[rt].valw;
    pushdown(rt);
    int mid=(L+R)>>1,val=0;
    if(l<=mid)val=max(val,query(lson,L,mid,l,r,p));
    if(mid<r)val=max(val,query(rson,mid+1,R,l,r,p));
    return val;
}
//找一条链最右边颜色为p的点的dfs序
int ask(int rt,int L,int R,int l,int r,int p){
    if(p?tree[rt].covb:tree[rt].covw)return 0;
    if(p?tree[rt].covw:tree[rt].covb)return min(r,R);
    if(L==R)return L;
    pushdown(rt);
    int mid=(L+R)>>1;
    if(mid<r){
        int tmp=ask(rson,mid+1,R,l,r,p);
        if(tmp)return tmp;
    }
    if(l<=mid)return ask(lson,L,mid,l,r,p);
    return 0;
}
//找x所在的连通块的根节点
int findfa(int x,int dep){
    for(int i=0;i<=__lg(n);i++){
        if((dep>>i)&1)x=Fa[x][i];
    }
    return x;
}
int chainfind(int x){
    int y=x,c=col[x];
    while(x){
        int tmp=ask(1,1,n,dfn[L[x]],dfn[x],c);
        if(tmp)return findfa(y,dep[y]-dep[rk[tmp]]-1);
        x=fa[L[x]];
    }
    return 1;
}
//4操作
void chainupdate(int x,int y,int val){
    while(L[x]!=L[y]){
        if(dep[L[x]]<dep[L[y]])swap(x,y);
        modi(1,1,n,dfn[L[x]],dfn[x],val);
        x=fa[L[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    modi(1,1,n,dfn[x],dfn[y],val);
}
int main(){
    freopen("astill.in","r",stdin);
    freopen("astill.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs1(1,0);dfs2(1,0,1);
    for(int i=1;i<=n;i++)scanf("%d",&col[i]);
    for(int i=1;i<=n;i++)scanf("%d",&val[i]);
    for(int i=1;i<=n;i++)ins(1,1,n,dfn[i],R[i],dfn[i],col[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int od,x,y,w;scanf("%d%d",&od,&x);
        if(od==1){
            del(1,1,n,dfn[x],R[x],dfn[x],col[x]);
            col[x]^=1;
            ins(1,1,n,dfn[x],R[x],dfn[x],col[x]);
            update(1,1,n,dfn[x]);
        }
        else if(od==2){
            scanf("%d",&w);
            x=chainfind(x);
            Modi(1,1,n,dfn[x],R[x],w,col[x]);
        }
        else if(od==3){
            x=chainfind(x);
            printf("%d\n",query(1,1,n,dfn[x],R[x],col[x]));
        }
        else if(od==4){
            scanf("%d%d",&y,&w);
            chainupdate(x,y,w);
        }
        else{
            scanf("%d",&w);
            modi(1,1,n,dfn[x],R[x],w);
        }
    }
    return 0;
}

标签:rt,val,int,题解,黑白,tree,valb,valw
From: https://www.cnblogs.com/gtm1514/p/17072848.html

相关文章

  • CF468E Permanent 题解
    考虑暴力状压DP。按行DP,记录列哪些被选过,可以做到\(O(2^kk^2)\)。注意到某一列扫完了之后这一列选没选过不重要,可以减少这里的状态。简单优化一下,每次选择最少的一列......
  • CF1033E 题解
    题意传送门交互题,给定一个简单连通图,你可以询问一个点集\(s\),返回其导出子图的边数。判断此图是否为二分图:若是,输出其中一部点的集合;否则输出任一个奇环。最多询问\(20......
  • Good Bye 2022 简要题解
    从这里开始比赛目录过气选手留下了只会套路的眼泪。sad......ProblemA KoxiaandWhiteboards相信大家都会.jpgCode#include<bits/stdc++.h>using......
  • 题解:【CF226D】The table
    题目链接调整构造。发现\(n\)和\(m\)较小只有\(100\),因此可以考虑尝试进行修改从而不断逼近答案。容易发现如果将某一行或列上的数字翻转,那么得到的新的和一定与原......
  • 【题解】ABC287
    \(\text{AtCoderBeginnerContest287}\)AMajority无意义题,问同意的是不是占半数以上。BPostalCard无意义题,对一个字符串集合开桶,对应匹配另一个字符串集合。CPa......
  • P3070 [USACO13JAN]Island Travels G 题解
    题目传送门一道耗费了本蒟蒻与某机房卷王半天的恶心题题目描述给定一个图,求每个X连通块之间的最短Hamilton路径。假如您不知道Hamilton路径是什么分析这题本质......
  • 【题解】P4491 [HAOI2018]染色
    思路NTT优化二项式反演。首先考虑到求“正好有\(k\)种颜色出现\(S\)次”的方案数,所以可以考虑转化成求“至少有\(k\)种颜色出现\(S\)次”的方案数。形式化......
  • ABC 287 (E-Ex) 题解
    E我的做法对于每个串枚举他的答案,然后直接hash硬干就完了。卡一卡就过去了#include<bits/stdc++.h>usingnamespacestd;typedefunsignedlonglongull;const......
  • setting.xml的mirror、mirrorOf和pom.xml的repositories、repository的关系关联snapsh
    setting.xml的mirror、mirrorOf和pom.xml的repositories、repository的关系关联snapshots带有时间错问题解决方案nexus3.8私有仓库https://blog.csdn.net/Michaelwubo/a......
  • [CF380C] Sereja and Brackets 题解
    [CF380C]SerejaandBrackets题解给定一个括号串\(s\)与\(m\)次询问。l,r回答字符串\(t=s_ls_{l+1}\dotss_r\)​的所有子序列中最长的合法括号串的长度。......