zoukankan      html  css  js  c++  java
  • BZOJ 3319: 黑白树 并查集 + 离线 + 思维

    Description

    给定一棵树,边的颜色为黑或白,初始时全部为白色。维护两个操作:
    1.查询u到根路径上的第一条黑色边的标号。
    2.将u到v    路径上的所有边的颜色设为黑色。
    Notice:这棵树的根节点为1

    Input

    第一行两个数n,m分别表示点数和操作数。
    接下来n-1行,每行2个数u,v.表示一条u到v的边。
    接下来m行,每行为以下格式:
    1 v 表示第一个操作
    2 v u 表示第二种操作
    n,m<=10^6

    Output

    对于每个询问,输出相应答案。如果不存在,输出0
    题解:我们将边下放到点,用点的标号来代指边
    看完题面后有一个想法:用并查集维护每个白点所能到达的第一个黑点
    我们知道,并查集是向上合并的
    如果正着做,每一次将一条路径染黑,会导致很多白点的祖先改变,而且是向下变
    向下变就十分麻烦,非常不好做
    不妨逆着操作,记录每一个节点变黑的最早时间,将操作逆着进行
    可以先将整棵树全部染黑,逆着逐渐染白,那么每个白点的祖先只会更向上,向上合并
    比如当前要将点 $x$ 染白,那么 $x$ 子树中的白点在改动前并查集指向的祖先都是 $x$
    在改动后 $x$ 的祖先会向上合并,而 $x$ 子树中的白点在并查集查祖先时查到 $x$ 的话会顺着 $x$ 向上合并到的祖先继续向上查询
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #include<stack>
    #include<queue>
    #define setIO(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)  
    #define maxn 1002002
    using namespace std;  
    char *p1,*p2,buf[100000];
    #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
    int rd() {int x=0; char c=nc(); while(c<48) c=nc(); while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x;}
    vector<int>G[maxn]; 
    stack<int>S;                  
    int n,m,edges; 
    int tm[maxn],hd[maxn<<1],to[maxn<<1],nex[maxn<<1];     
    int siz[maxn],top[maxn],fa[maxn],dep[maxn],son[maxn]; 
    int idx[maxn<<1],id[maxn<<1];     
    inline void addedge(int u,int v,int c) {
        nex[++edges]=hd[u],hd[u]=edges,to[edges]=v, id[edges]=c;   
    }
    void dfs1(int u,int ff) {
        dep[u]=dep[ff]+1,siz[u]=1,fa[u]=ff; 
        for(int i=hd[u];i;i=nex[i]) {
            int v=to[i];     
            if(v==ff) continue; 
            idx[v]=id[i], dfs1(v,u), siz[u]+=siz[v]; 
            if(siz[v] > siz[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]) {
            int v=to[i]; 
            if(v==fa[u] || v==son[u]) continue; 
            dfs2(v,v); 
        }
    }
    inline 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; 
    }
    struct Opt {
        int opt,x,y;
    }op[maxn]; 
    struct Union {
        int p[maxn]; 
        inline void init() {
            for(int i=0;i<maxn;++i) p[i]=i; 
        }
        int find(int x) {
            return p[x]==x?x:p[x]=find(p[x]); 
        }
    }black,white;         
    inline void mark(int u,int lca,int cur) {
        u=tm[u]?black.find(u):u;  
        while(dep[u]>dep[lca]) {
            tm[u]=cur;      
            if(!tm[fa[u]]) {
                black.p[u]=fa[u];   
                u=fa[u];   
            }    
            else {
                int y=black.find(fa[u]);          
                black.p[u]=y;          
                u=y;                  
            }
        }
    }
    inline void update(int u,int v,int cur) {
        int lca=LCA(u,v); 
        mark(u,lca,cur), mark(v,lca,cur);     
    }
    inline void change(int u) {
        white.p[u]=white.find(fa[u]);                
    }
    int main() {
        // setIO("input"); 
        scanf("%d%d",&n,&m); 
        for(int i=1;i<n;++i) {
            int u,v; 
            u=rd(),v=rd(); 
            addedge(u,v,i),addedge(v,u,i);         
        }
        dfs1(1,0), dfs2(1,1), black.init();                      
        for(int i=1;i<=m;++i) {
            op[i].opt=rd(); 
            if(op[i].opt==1) op[i].x=rd(); 
            if(op[i].opt==2) op[i].x=rd(), op[i].y=rd(), update(op[i].x,op[i].y,i);  
        }   
        white.init(); 
        for(int i=2;i<=n;++i) G[tm[i]==0?m+1:tm[i]].push_back(i);  
        m+=(G[m+1].size()>1); 
        for(int i=m;i>=1;--i) {      
            if(G[i].size()) {
                for(int j=0;j<G[i].size();++j) change(G[i][j]);        
            }else if(op[i].opt==1) {
                S.push(white.find(op[i].x));                 
            }
        }             
        while(!S.empty()) {
            printf("%d
    ",idx[S.top()]); S.pop(); 
        }
        return 0; 
    }
    

      

  • 相关阅读:
    api封装
    计算字符串内的字出现几次
    Generator 函数的语法
    js原型链的看法
    购物车之上线版思路
    Java并发多线程面试题 Top 50
    Java NIO和IO的区别
    【转载】线程池的原理及实现
    【转载】关于SimpleDateFormat安全的时间格式化线程安全问题
    【转载】Java对象的生命周期
  • 原文地址:https://www.cnblogs.com/guangheli/p/11235798.html
Copyright © 2011-2022 走看看