CF455C Civilization

@Pelom  October 11, 2021

题意

给出一个森林,$2$种操作

  • 求$u$所在的树的直径
  • 合并$u,v$所在的树并使直径最小

数据范围:$1 \le m < n \le 3 \cdot 10^5$

题解

由合并操作想到并查集
对于第一个操作,输出$u$所在的联通块(树)的直径
对于第二个操作,合并后的直径最小为

  • $u$所在树的直径
  • $v$所在树的直径
  • $u$所在树的半径$+v$所在树的半径$+1$

取$\max$

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e5+10;
int n,m,q,u,v,op;
int cnt,len,t;
int head[N],fa[N],dis[N],clr[N];
struct edge{
    int nxt,to;
} e[N<<1];
inline void add(int u,int v){
    e[++cnt]=edge{head[u],v};
    head[u]=cnt;
}
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void merge(int x,int y){
    if(find(x)==find(y))
        return ;
    fa[fa[x]]=fa[y];
}
void dfs(int u,int c,int l){
    if(clr[u]==c)
        return ;
    if(len<l){
        len=l;
        t=u;
    }
    clr[u]=c;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        merge(u,v);
        dfs(v,c,l+1);
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    for(int i=1;i<=n;i++)
        if(!clr[i]){
            len=0,t=i;
            dfs(i,i,0);
            len=0;
            dfs(t,t,0);
            dis[find(i)]=len;
        }
    for(;q--;){
        scanf("%d",&op);
        if(op==1){
            scanf("%d",&u);
            printf("%d\n",dis[find(u)]);
        }
        else{
            scanf("%d%d",&u,&v);
            u=find(u),v=find(v);
            if(u==v)
                continue;
            fa[u]=v;
            dis[v]=max((dis[u]+1>>1)+(dis[v]+1>>1)+1,max(dis[u],dis[v]));
        }
    }
    return 0;
}

添加新评论