题意简述&数据
(交了翻译不知道过没过)
懒得打字了:2333
样例
输入
6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
输出
4
思路
对于两个集合,如果我们要实现"智能合并"操作,最优的策略肯定是这样的:
选择两个集合的直径(即最长不重复路径)上的中点,合并。设两边的商都分别是
s
u
,
s
v
su,sv
su,sv,且不妨令
s
u
>
=
s
v
su>=sv
su>=sv,那么
- 合并后的直径长度为 s u su su,因为 s u su su过于长
- s u , s v su,sv su,sv相对平衡,导致合并后的长度是 ( s u + 1 ) / 2 + ( s v + 1 ) / 2 + 1 (su+1)/2+(sv+1)/2+1 (su+1)/2+(sv+1)/2+1,即取中点
那么,如何处理初始的边呢?
就相当于求一个图(珂能有环)的直径。类比树的直径求法,我们设计这样一个算法:
- 以任意一个点为开始,找到最远的点
- 从这个最远的点开始,再找到一个最远的点,就是直径的两个端点
那么,这样对么?显然是对的,证明方法:
意会
好了上代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 300100
class Graph//图
{
public:
int head[N];
int EdgeCount;
struct Edge
{
int To,Label,Next;
}Ed[N<<1];
void clear()
{
memset(Ed,-1,sizeof(Ed));
memset(head,-1,sizeof(head));
EdgeCount=0;
}
void AddEdge(int u,int v,int w)
{
++EdgeCount;
Ed[EdgeCount]=(Edge){v,w,head[u]};
head[u]=EdgeCount;
}
int Start(int u)
{
return head[u];
}
int To(int u)
{
return Ed[u].To;
}
int Label(int u)
{
return Ed[u].Label;
}
int Next(int u)
{
return Ed[u].Next;
}
}G;void Add(int u,int v,int w){G.AddEdge(u,v,w);G.AddEdge(v,u,w);}
class DSU//并查集
{
public:
int Father[N],Cnt[N];
void Init()
{
for(int i=0;i<N;i++)
{
Father[i]=i;
Cnt[i]=1;
}
}
int Find(int x)
{
return (x==Father[x])?x:(Father[x]=Find(Father[x]));
}
}D;
int n,m,q;
void Input()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;++i)
{
int u,v;scanf("%d%d",&u,&v);
Add(u,v,1);
}
}
int maxlen[N];
int ans=-1;
int root;
int furthest;
void DFS(int u,int pre,int dis)//找到最远的点
{
D.Father[u]=root;
if (dis>ans)
{
ans=dis;
furthest=u;
}
for(int i=G.Start(u);~i;i=G.Next(i))
{
int v=G.To(i);
if (v!=pre)
{
DFS(v,u,dis+1);
}
}
}
void IntelligentMerge(int u,int v)//智能合并
{
int au=D.Find(u),av=D.Find(v);
if (au==av) return;
int su=maxlen[au],sv=maxlen[av];
if (su<sv)
{
swap(maxlen[au],maxlen[av]);
swap(su,sv);
}
D.Father[av]=au;
maxlen[au]=max(su,(su+1)/2+(sv+1)/2+1);
}
#define IMerge IntelligentMerge//。。。不好意思这个名字真的很长
void Soviet()//awsl 爱我苏联
{
for(int i=1;i<=n;++i)
{
if (D.Father[i]==i)
{
ans=-1;
root=furthest=i;
DFS(root,-1,0);
ans=-1;
DFS(furthest,-1,0);
maxlen[i]=ans;
}
}
for(int i=1;i<=q;++i)
{
int o;scanf("%d",&o);
if (o==2)
{
int u,v;scanf("%d%d",&u,&v);
IMerge(u,v);
}
else if (o==1)
{
int u;scanf("%d",&u);
printf("%d\n",maxlen[D.Find(u)]);
}
}
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
D.Init();
G.clear();
Input();
Soviet();
}
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}
博客介绍了CF 455C题目,涉及图的直径计算和并查集的智能合并策略。通过选择两个集合直径上的中点进行合并,以优化合并过程。文章提供了样例输入和输出,以及利用直径找图中两点最远距离的算法,证明了这种方法的正确性。
5万+

被折叠的 条评论
为什么被折叠?



