题目大意
%
给定一个森林,边权为1,支持以下两种操作
1
x
y
\texttt{1 x y}
1 x y 若节点x和节点y不在同一个连通块内,你需要在节点x所在连通块和节点y所在的连通块中分别找一个点,连接一条边权为1的边,使得最终的连通块内的直径尽量长。
2
x
\texttt{2 x}
2 x 输出节点x所在连通块中的直径长度。
数据范围
1
⩽
n
⩽
3
×
1
0
5
1\leqslant n\leqslant 3\times 10^5
1⩽n⩽3×105
题解
%
先求出每个连通块内的直径长度,然后用并查集维护联通信息,对于操作2,,我们考虑连接两个连通块各自的直径的重点,则可以发现,最终得到的直径长度不会超过
⌈
d
i
a
m
e
t
e
r
[
a
n
c
e
s
t
o
r
[
x
]
]
2
⌉
+
⌈
d
i
a
m
e
t
e
r
[
a
n
c
e
s
t
o
r
[
y
]
]
2
⌉
+
1
\left\lceil\frac{diameter[ancestor[x]]}{2}\right\rceil+\left\lceil\frac{diameter[ancestor[y]]}{2}\right\rceil+1
⌈2diameter[ancestor[x]]⌉+⌈2diameter[ancestor[y]]⌉+1 用上式和
d
i
a
m
e
t
e
r
[
a
n
c
e
s
t
o
r
[
x
]
]
,
d
i
a
m
e
t
e
r
[
a
n
c
e
s
t
o
r
[
y
]
]
diameter[ancestor[x]],diameter[ancestor[y]]
diameter[ancestor[x]],diameter[ancestor[y]] 取最大值即可。时间复杂度为
Θ
(
n
α
(
n
)
)
\Theta(n\alpha(n))
Θ(nα(n)),下面的代码没有使用按秩合并,因而时间复杂度为
Θ
(
n
log
2
n
)
\Theta(n\log_2 n)
Θ(nlog2n)。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 300010
struct edge{
int v,next;
}edges[maxn<<1];
int n,m,q,head[maxn];
void ins(int u,int v){
static int cnt=0;
edges[++cnt]=(edge){v,head[u]};
head[u]=cnt;
}
int f[maxn],g[maxn],vis[maxn];
void dfs(int u,int fa=-1){
vis[u]=true;
for(int i=head[u];i;i=edges[i].next){
int v=edges[i].v;
if(v==fa||vis[v]) continue;
dfs(v,u);
f[u]=max(f[u],g[v]+g[u]+1);
f[u]=max(f[u],f[v]);
g[u]=max(g[u],g[v]+1);
}
}
int fa[maxn];
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
ins(u,v);ins(v,u);
int fx=find(u),fy=find(v);
if(fx!=fy) fa[fx]=fy;
}
for(int u=1;u<=n;u++){
int fu=find(u);
if(!vis[fu]) dfs(fu);
}
for(int i=1,op;i<=q;i++){
scanf("%d",&op);
if(op==1){
int x; scanf("%d",&x);
printf("%d\n",f[find(x)]);
}else{
int x,y; scanf("%d%d",&x,&y);
int fx=find(x),fy=find(y);
if(fx!=fy){
f[fx]=max((f[fx]+1>>1)+(f[fy]+1>>1)+1,max(f[fx],f[fy]));
fa[fy]=fx;
}
}
}
return 0;
}