CF455C Civilization | luogu HXY造公园

题目链接:
https://www.luogu.org/problemnew/show/P2195
http://codeforces.com/contest/455/problem/C

显然我们可以想到要用并查集来维护联通节点的信息。

然后题目要求的是合并之后树的直径最小。

然后我们可以知道在两个树的中点合并是最好的。所以我们设\(len[i]\)为i所在树的直径,那么按照上述策略,合并\(i,j\)之后新树的直径就是\(max(len[i],len[j],(len[i]+1)/2+(len[j]+1)/2+1)\)

接下来就是。。树的直径怎么求?

两遍bfs或者dfs就行了qwq

第一遍先找到当前点最远的点pos,之后从pos开始找离pos最远的点到pos的距离就是树的直径了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 300010
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int n,m,que,edge_number,pos,ans,root;
int fa[MAXN],len[MAXN],head[MAXN],done[MAXN],dis[MAXN];
struct Edge{int nxt,to;}edge[MAXN<<1];
inline int maxx(int x,int y,int z){return max(max(x,y),z);}
inline void add(int from,int to)
{
    edge[++edge_number].to=to;
    edge[edge_number].nxt=head[from];
    head[from]=edge_number;
}
inline int find(int x)
{
    if(fa[x]==x) return x;
    else return fa[x]=find(fa[x]);
}
inline void merge(int x,int y)
{
    int a=find(x),b=find(y);
    if(a==b) return;
    if(len[a]<len[b]) swap(a,b);
    fa[b]=a; 
    len[a]=maxx(len[a],len[b],(len[a]+1)/2+(len[b]+1)/2+1);
}
inline void search(int now,int pre,int sum)
{
    fa[now]=root;
    if(sum>ans)
        ans=sum,pos=now;
    for(int i=head[now];i;i=edge[i].nxt)
        if(edge[i].to!=pre)
            search(edge[i].to,now,sum+1);
}
int main()
{
    n=read(),m=read(),que=read();
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        u=read(),v=read();
        add(u,v);
        add(v,u);
    }
    for(int i=1;i<=n;i++)
    {
        if(fa[i]!=i) continue;
        root=pos=i; 
        ans=0; 
        search(i,0,0);
        ans=0; 
        search(pos,0,0);
        len[i]=ans;
    }
    for(int i=1;i<=que;i++)
    {
        int name,u,v,k;
        name=read();
        if(name==2)
            u=read(),v=read(),merge(u,v);
        else 
            k=read(),printf("%d\n",len[find(k)]);
    }
    return 0;
}

转载于:https://www.cnblogs.com/fengxunling/p/9915351.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值