标签:com std space continue 连通 部分 href span init
给定一n个点、m条边的森林,q次操作,操作分两种:1.给定一个点x,要求x所在的树的直径;2.给定两个点x,y,选取x所在树中的一个点u,y所在树中的一个点v,新增一条边(u,v),合并两棵树,使得合并后的新树的直径最小。
对于初始的森林,显然可以dp一遍求出所有树的原始直径。
先给所有无根树定根,可以用并查集维护连通性,然后以祖先节点为根。
以len[i]表示以i为根的这棵树的直径,则操作一只需并查集找到祖先节点,然后输出该点的len即可。
主要看操作二。
合并两棵树,连通性依然能够用并查集继续维护,关键是合并后新树的直径如何得到。
首先,设合并前x所在树的直径为d1,y所在树的直径为d2,则新树的直径d必然满足d>=max(d1,d2),否则max(d1,d2)为直径,矛盾!
设我们新加的边为(u,v),于是我们可以把新树的所有可能成为直径的路径分为两类:
1.原两棵树内的路径,最长即为原两棵树的直径,即d1,d2。
2.经过(u,v)的路径。
第一类已求,故我们只需求出第二类即可。
由于我们要使新树的直径最小,所以对于加边之前u所在的树,我们所取的u需要满足树中距离u最远的点与u的距离最小(意即:设dis[i]为i所在树中距离i最远的点与i的距离,则对于树中除u外所有点i,需要满足dis[u]<=dis[i],即u的dis值为最小)。
那么不难发现一个结论:u必然在其所在树的直径上。反证:若u不在其所在树的直径上,设直径上距离u最近的点为p,设u与p的距离为x,点p将直径分为两部分,设两部分的长度分别为l1,l2。则根据定义,dis[u]=x+max(l1,l2),而dis[p]=max(l1,l2)<=dis[u],则u不是dis值最小的点,矛盾!
所以,u在其所在树的直径上,那么u便会将直径分为两部分l1,l2,因为我们要加入路径中的长度是max(l1,l2),所以我们要使max(l1,l2)尽量小,那么只要使得u尽量靠近直径的中点即可,即d1/2上取整,算术表达式即为(d1+1)/2。
那么u的部分我们就求完了,v的部分同理即可。
所以最后第二类的答案即为(d1+1)/2+(d2+1)/2+1。
最后与第一类答案取一个max然后更新树的直径即可。
时间复杂度为O((n+m+q)logn)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=3e5+10,M=3e5+10;
struct Edge{
int to,next;
}edge[M<<1];int idx;
int h[N];
void add_edge(int u,int v){edge[++idx]={v,h[u]};h[u]=idx;}
int len[N],vis[N];
int d[N],f[N];
int fa[N];
int n,m,q;
void init()
{
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)fa[i]=i;
}
int get(int x)
{
if(fa[x]==x)return x;
return fa[x]=get(fa[x]);
}
void dp(int p)
{
vis[p]=1;
for(int i=h[p];~i;i=edge[i].next)
{
int to=edge[i].to;
if(vis[to])continue;
dp(to);
f[p]=max(f[p],d[p]+d[to]+1);
d[p]=max(d[p],d[to]+1);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
init();
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
x=get(x),y=get(y);
fa[x]=y;
}
for(int i=1;i<=n;i++)
{
if(!vis[i])dp(get(i));
int u=get(i);
len[u]=max(len[u],f[i]);
}
while(q--)
{
int tp,x,y;
scanf("%d%d",&tp,&x);
if(tp==1)
{
x=get(x);
printf("%d\n",len[x]);
continue;
}
scanf("%d",&y);
x=get(x),y=get(y);
if(x==y)continue;
int d1=len[x],d2=len[y];
len[y]=max(d1,d2);
len[y]=max(len[y],((d1+1)>>1)+((d2+1)>>1)+1);
fa[x]=y;
}
return 0;
}
标签:com std space continue 连通 部分 href span init
原文地址:https://www.cnblogs.com/ninedream/p/13769638.html