题意:给你n个城市,然后告诉你m条路,然后q个询问。第一个询问是让你输出该城市所在的区域的最长路。第二询问是让你把两个城市所在区域,并使得合并后的区域最长路最短。
条件:1 第二询问是要使得合并后的区域最长路最短
2 区域就是与该城市相连所有的城市构成的集合
思路:这题最难的地方在于求最开始最长路和使得合并后的区域最长路最短。求最开始的最长路可以采用两次dfs,dfs找到一个区域最长路,一个点对应最长路的终点,那个终点的最长路就是该区域的最长路。求合并后的区域最长路最短,两个区域合并最长路可以是原来两个区域的最长路,也可以是原先最长的要么是中点相连。
代码:
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int maxn=3*100000+5;//可以写成1e5
vector<int>mp[maxn];//因为直接开数组会爆
int vis[maxn];//并查集
int s[maxn];//记录区域最大的路长
int father,lst,ans;
int getfather(int x)//找祖先
{
if(vis[x]==x)
return x;
else
return vis[x]=getfather(vis[x]);
}
int dfs(int a,int pre,int step)//dfs找最大路
{
vis[a]=father;//所有通路的祖先都是第一
if(ans<step)//如果到这个点的路径比最大的长
{
ans=step;
lst=a;
}
for(int i=0;i<mp[a].size();i++)//寻找通路的下一个,但是不返回
{
if(mp[a][i]!=pre)
{
dfs(mp[a][i],a,step+1);
}
}
}
void link(int x,int y)//连接
{
int a,b;
a=getfather(x);
b=getfather(y);
if(a==b)
return ;
if(s[a]<s[b])
swap(s[a],s[b]);
vis[b]=a;
s[a]=max(s[a],(s[a]+1)/2+(s[b]+1)/2+1);//会发现最小的最长路,要么是原先最长的要么是中点相连
}
int main()
{
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
{
vis[i]=i;
}
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
mp[x].push_back(y);
mp[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
if(vis[i]==i)
{
father=lst=i;
ans=-1;
dfs(i,0,0);
ans=-1;
dfs(lst,0,0);
s[i]=ans;
}
}
while(q--)
{
int a,x,y;
scanf("%d",&a);
if(a==1)
{
scanf("%d",&x);
int a=getfather(x);
printf("%d\n",s[a]);
}
else if(a==2)
{
scanf("%d%d",&x,&y);
link(x,y);
}
}
return 0;
}
总结:1 刚开始的时候看错题了,没有看到最长路的最小,英语有待提高
2 没有仔细得去找规律和思考