题目:
银河英雄传说
题意解释:
起始有3w艘战舰,每条战舰独自成一列,共3w列,输入Mij指令将第i号战舰所在的整列放到j号战舰后,Cij查询i号战舰和j号战舰中间有多少战舰,若ij不在同一列返回-1。(每个战舰看成一个结点)
解题思路:
fa[]数组存放每个点的父亲节点
value[]数组存放该点到头结点之间共有多少结点(不包括该点)
tot[]数组存放整列共有多少结点,放在头结点位置
使用带权并查集,边权存该结点到头结点(包括头结点,但不包括该点),一共有多少结点,同时头结点存放整列共有多少结点。路径压缩时直接按照带权并查集的处理方式即可,合并集合时除了带权并查集的合并之外,还要更新头结点的tot数。
代码:
#include<bits/stdc++.h>
#define maxn 30010
using namespace std;
int fa[30010],value[30010],tot[30010];//tot用于存放这一列一共有多少节点,放在头结点
//并查集核心系操作,寻找父亲节点
int findFa(int x)
{
if(x==fa[x])return x;//只有父亲节点才为本身
int new_fa=findFa(fa[x]);//new_fa存放找到的父亲节点
value[x]+=value[fa[x]];//x的权值加上父亲的权值
fa[x]=new_fa;
return new_fa; //返回new_fa
}
int main()
{
for(int i=1; i<=30000; i++)
{
fa[i]=i,value[i]=0,tot[i]=1;//初始时每一个节点都是头结点
}
int T,x,y;
char ch;
scanf("%d",&T);
while(T--)
{
scanf("%s%d%d",&ch,&x,&y);
if(ch=='M')//M进行合并
{
value[findFa(x)]+=tot[findFa(y)];//更新x的父亲节点fa[x]的权值
tot[fa[y]]+=tot[fa[x]];//更新y的父亲节点fa[y]的tot值
fa[fa[x]]=fa[y];//将x的父亲节点fa[x],指向y的父亲节点fa[y]
}
else //C进行查询
{
if(findFa(x)==findFa(y))//x和y在同一列
{
cout<<abs(value[x]-value[y])-1<<endl;
}
else //不在同一列
{
cout<<-1<<endl;
}
}
}
return 0;
}
代码图解:
findFa()方法图解每次找父亲结点,并且路径压缩后的结果:
大家注意第一次value[A]=1而不是2,这是因为我将B连接到C只是更新的B的value
还没有更新valueA,只有等下一次执行findFa()的时候我在压缩路径,将valueA变为2。大家可以拿笔找着代码执行以下就知道了!