zoukankan      html  css  js  c++  java
  • [ BZOJ 4668 ] 冷战


    (\)

    (Description)


    (N)个点,开始没有边相连,进行按顺序给出的(M)个操作:

    • (0 u v)(u,v)两点连一条边
    • (1 u v) 查询(u,v)两点最早在第几条边连接的时候被连通

    每次询问输出一个边的编号,强制在线。

    • (N,Min [1,5 imes 10^5])

    (\)

    (Solution)


    在线并查集树上查询(Lca)

    维护连通性的时候并查集不进行路径压缩,只进行按秩合并。考虑到并查集是树形结构,定义连通块的秩为块内树高(()其实定义为块的大小表现也不错())。这样我们得到的是一棵真正的通过并集来连接的并查集树。

    这棵树上有什么好的性质?答案是两点到(Lca)的路径上的边集,是真正将这两点连接起来的边集。也就是说,对于两点查询的答案,一定是两点到(Lca)路径上的最大边编号。

    考虑如何求(Lca)。因为是在线,所以显然不能建立倍增数组等基于固定的树形态的做法。考虑暴力标记,先将两点跳到同一高度,再共同跳到(Lca)处既可,这样也能很方便的处理路径(max)的问题。

    关于复杂度,其实它是合法的。根据按秩合并的原理,在查询连通块代表元素时复杂度是(log)级别的,同理都是跳父节点的过程,所以查询(Lca)复杂度也是(log)级别的。

    (\)

    (Code)


    并查集需要维护:当前节点深度,当前节点父节点编号,到父节点的边权,以及以当前节点为根子树大小。

    注意当前节点深度这个部分,在查询(Lca)之前我们需要先更新一遍以确保深度是正确的,这个过程可以在查询代表元素的时候同时进行。

    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 500010
    #define R register
    #define gc getchar
    using namespace std;
     
    int n,m,ans,cnt;
     
    struct UFS{
      int f[N],g[N],d[N],sz[N];
     
      UFS(){for(R int i=1;i<N;++i)f[i]=i,d[i]=sz[i]=1;}
     
      int find(int x){
        if(x==f[x])return x;
        int ans=find(f[x]); d[x]=d[f[x]]+1; return ans;
      }
     
      inline void merge(int x,int y){
        int fx=find(x),fy=find(y);
        if(sz[fx]>sz[fy]) fx^=fy^=fx^=fy;
        sz[fy]+=sz[fx]; f[fx]=fy; g[fx]=cnt; d[fx]=d[fy]+1;
      }
     
      inline int lca(int x,int y){
        int ans=0;
        if(d[x]>d[y]) x^=y^=x^=y;
        while(d[y]>d[x]) ans=max(ans,g[y]),y=f[y];
        if(x==y) return ans;
        while(x!=y){
          ans=max(ans,max(g[x],g[y]));
          x=f[x]; y=f[y];
        }
        return ans;
      }
     
    }ufs;
     
    inline int rd(){
      int x=0; bool f=0; char c=gc();
      while(!isdigit(c)){if(c=='-')f=1;c=gc();}
      while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
      return f?-x:x;
    }
     
    int main(){
      n=rd(); m=rd();
      for(R int i=1,op,x,y;i<=m;++i){
        op=rd(); x=rd()^ans; y=rd()^ans;
        if(op==0){
          ++cnt;
          if(ufs.find(x)!=ufs.find(y)) ufs.merge(x,y);
        }
        else if(ufs.find(x)!=ufs.find(y)){ans=0;puts("0");}
             else printf("%d
    ",(ans=ufs.lca(x,y)));
      }
      return 0;
    }
    
  • 相关阅读:
    Linux系统目录结构介绍
    【Android开发学习笔记】【第二课】Activity学习
    【Android开发学习笔记】【第一课】初识New Project,工程文件介绍
    Android 环境快速搭建-详细步骤-win7-64bit
    【VC6】【集成工具】将输入信息集成到VC工具中
    Intellij IDEA快捷键
    C#+Winform开发窗体程序
    使用U盘重装系统
    C#进阶学习笔记
    C#基础学习笔记
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9748093.html
Copyright © 2011-2022 走看看