CF455C. Civilization-并查集

2100分的并查集(x)
link:https://codeforces.com/contest/455/problem/C
给一张无向森林,有若干次操作,有两种:

  • 询问 x 所在树的直径
  • 合并 x,y 所在的连通块,使得合并后的直径最小

n,m,q3×105


处理出每个连通块的直径,考虑如何合并两个连通块?设原来的直径分别是 L1,L2,合并后的直径至少是 max(L1,L2),我们肯定不希望变得太大,手摸一下会发现至少还会是 L1/2+L2/2+1,对三个值取max

直径的信息可以用并查集维护

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=3e5+5;
int n,m,q,c;
int fa[N],len[N],dep[N];
vector<vector<int>> G;
int find(int x){
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return;
int L1=len[fx],L2=len[fy];
len[fx]=max({L1,L2,1+(L1+1)/2+(L2+1)/2});
fa[fy]=fx;
}
void dfs(int x,int fa){
for(auto to:G[x])if(to!=fa){
dep[to]=dep[x]+1;
if(dep[to]>dep[c])c=to;
dfs(to,x);
}
}
int main(){
fastio;
cin>>n>>m>>q;
rep(i,1,n)fa[i]=i;
G=vector<vector<int>>(n+1);
while(m--){
int a,b;
cin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
merge(a,b);
}
rep(i,1,n)if(find(i)==i){
int L,R;
c=0;
dfs(i,-1);
L=c;
dep[c]=0;
dfs(c,-1);
R=c;
len[i]=dep[R]-dep[L];
}
while(q--){
int op,x,y;
cin>>op>>x;
if(op==1)cout<<len[find(x)]<<endl;
else{
cin>>y;
merge(x,y);
}
}
return 0;
}
posted @   yoshinow2001  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示