洛谷P2195 HXY造公园

题目
题意
给出一个n个点,m条边组成的森林,有q组询问:
1.给出点x,求点x所在的树的直径
2.给出点x,y,要求将x,y所在的树之间连一条边并构成一棵新的树,满足这个新的树的直径最小
思路
1.求树的直径,并存在一个数组里
2.用并查集来动态合并+维护区域信息(包括同一颗树里的有着相同祖先的点的合并,不同树之间的合并)
3.假设length数组:对于每棵树的根节点x,length[x]=该树的直径长度
接下来对于每个询问2(如果给出的两点在同一颗树内则忽略),利用并查集找出两棵树的根节点x,y,并用并查集合并两棵树,
合并后树的直径为

max{[(length[x]+1)/2+(length[y]+1)/2+1,length[x],length[y]}
因为要想直径最短,我们选择加边的点一定要在直径上,因为其他的点走到直径还要一段距离,从而增长了路径。

那么直径就被选择的点分成了两段。因为我们要最小化较长的那一段,所以要让选择的点尽量靠近直径的中点。最后的答案就是 直径长度的一半向上取整

并且还要考虑原先两棵树本来就存在的直径,他们仨进行比较,最大的才是合并后的树的直径
代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
struct Edge
{
    int from,to;
};

vector<Edge>edges;
vector<int>G[maxn];
void add(int from,int to)
{
    edges.push_back({from,to});
    int m=edges.size();
    G[from].push_back(m-1);
}

bool vis[maxn],vis2[maxn];
int pos,cnt;
int length[maxn],fa[maxn];

void dfs(int x,int len)
{
    if(len>cnt)
    {
        pos=x;
        cnt=len;
        //cout<<1<<endl;
    }
    vis[x]=true;
    for(int i=0;i<G[x].size();i++)
    {
        Edge e=edges[G[x][i]];
        int u=e.to;
        if(!vis[u])
        {
            dfs(u,len+1);
        }
    }
    vis[x]=false;
}

int cal(int x)//找树的直径
{
    cnt=-1;
    dfs(x,0);
    cnt=-1;
    dfs(pos,0);
    return cnt;
}

int find (int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

void merge(int x,int y)
{
    int xx=find(x),yy=find(y);
    fa[xx]=yy;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    memset(vis,0,sizeof(false));
    memset(vis2,0,sizeof(false));
    int n,m,q;
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        add(x,y);
        add(y,x);
        merge(x,y);
    }
    for(int i=1;i<=n;i++)
    {
        if(fa[i]!=i||vis2[i])//因为有并查集的存在,所以只对树的根节点进行操作
            continue;
        vis2[i]=true;//标记
        length[i]=cal(i);
    }
    while(q--)
    {
        int op,x,y;
        cin>>op>>x;
        if(op==1)
        {
            cout<<length[find(x)]<<endl;
            continue;
        }
        else
        {
            cin>>y;
            int xx=find(x),yy=find(y);
            if(xx==yy)
                continue;
            int temp=((length[xx]+1)/2)+((length[yy]+1)/2)+1;
            temp=max(temp,max(length[xx],length[yy]));
            merge(x,y);//合并
            length[yy]=temp;//更新
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值