HXY造公园

水题愉悦身心

一、题目

点此看题

二、解法

本题的关键问题就是维护树的直径,一开始可以跑一个树形 d p dp 求直径,我们考虑合并两颗树。

最优的方式就是取两颗树直径的中点,然后链接,具体就是:
l e n [ y ] = l e n [ y ] + 1 2 + l e n [ x ] + 1 2 + 1 len[y]=\frac{len[y]+1}{2}+\frac{len[x]+1}{2}+1 然后用并查集随便写一写就过了,时间复杂度近似线性(有一个反阿克曼常数)。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 300005;
int read()
{
 int x=0,flag=1;char c;
 while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
 while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
 return x*flag;
}
int n,m,q,tot,f[M],p[M],vis[M],dis[M][2],len[M];
struct edge
{
    int v,next;
    edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
void dfs(int u,int rt)
{
    vis[u]=1;
    for(int i=f[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(vis[v]) continue;
        dfs(v,rt);
        if(dis[v][0]+1>dis[u][0])
        {
            dis[u][1]=dis[u][0];
            dis[u][0]=dis[v][0]+1;
        }
        else
            dis[u][1]=max(dis[u][1],dis[v][0]+1);
    }
    len[rt]=max(len[rt],dis[u][0]+dis[u][1]);
}
int findSet(int x)
{
    if(p[x]^x) p[x]=findSet(p[x]);
    return p[x];
}
void merge(int u,int v)
{
    int x=findSet(u),y=findSet(v);
    p[x]=y;
}
int main()
{
    n=read();m=read();q=read();
    for(int i=1;i<=n;i++)
        p[i]=i;
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        e[++tot]=edge(v,f[u]),f[u]=tot;
        e[++tot]=edge(u,f[v]),f[v]=tot;
        merge(u,v);
    }
    for(int i=1;i<=n;i++)
        if(i==findSet(i))
            dfs(i,i);
    while(q--)
    {
        int op=read(),x=read();
        if(op==1)
            printf("%d\n",len[findSet(x)]);
        else
        {
            int y=read();
            if(findSet(x)==findSet(y)) continue;
            int a=findSet(x),b=findSet(y);
            len[b]=max(max(len[a],len[b]),(len[a]+1)/2+(len[b]+1)/2+1);
            p[a]=b;
        }
    }
}
发布了257 篇原创文章 · 获赞 13 · 访问量 6754

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/104108745
今日推荐