并查集经典题目
P1196 [NOI2002]银河英雄传说
链接戳这里☞
先不说什么,直接上代码吧!
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
struct sd{
int loc,len,x;
}node[300003];
int njc(int n)
{
if(n<0) return -n;
return n;
}
int father(int v)
{
if(v!=node[v].x)
{
int i=node[v].x;
node[v].x=father(node[v].x);
node[v].loc+=node[i].loc-1;
}
return node[v].x;
}
void modify(int a,int b)
{
int a1=father(a),b1=father(b);
node[a1].x=b1;
node[a1].loc=node[b1].len+1;
node[b1].len+=node[a1].len;
}
void query(int a,int b)
{
int a1=father(a),b1=father(b);
if(a1!=b1)
printf("-1
");
else
{
int ans=node[a].loc-node[b].loc;
printf("%d
",njc(ans)-1);
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=300000;++i)
{
node[i].len=1;
node[i].loc=1;
node[i].x=i;
}
int a,b;
char c[2];//☣
for(int i=1;i<=n;++i)
{
scanf("%s",c);
scanf("%d%d",&a,&b);
if(c[0]=='M')
{
modify(a,b);
}
if(c[0]=='C')
{
query(a,b);
}
}
return 0;
}
看完了吧!
我觉得大佬可以跳过这一段了。
下面让我们来讲一讲本题的重点:
对于father函数在本题应用的理解
这个father和以前使用的while版不一样
while版father函数部分:
int father(int v)
{
int i,j=v;
while(v!=x[v])
v=x[v];
while(j!=x[j])
{
i=j;
j=x[j];
x[i]=v;
}
return v;
}
递归版father函数部分:
int father(int v)
{
if(v!=node[v].x)
{
int i=node[v].x;
node[v].x=father(node[v].x);
node[v].loc+=node[i].loc-1;
}
return node[v].x;
}
特别注意!!!!
**在下面这段话中“前面”指:靠近儿子的信息
“后面”指靠近father的信息**
这里主要区别就是,递归版的father不需要在写一个while来进行路径压缩,他直接递归到最后一次,然后所有儿子的father就变成了根节点,然后只需要维护一下位置信息就可以了,并且因为他有一个回溯的过程,所以说不会存在while的空中楼阁问题(指用后面的信息维护了前面的点,但是因为while是顺着循环的,他没有递归的回溯过程,所以有可能会出现前面的被后面的信息维护后,后面的信息又被更改了!)(这段话写的我好难受!!!)
维护语句:
node[v].loc+=node[i].loc-1;
自己原来的位置+自己father的位置=自己现在的位置。
P.s:
1、有人可能会问我,维护语句感觉好像没有什么用啊?其实他是为了和modify一起配合使用,当一个舰队接入另一个舰队的时候,在modify中我们会更改要接入的头的爸爸,而这时,进行father的时候就可以把跟着第二支舰队旗舰的后面的战舰的位置用维护语句更新他们的位置。
2、定义的字符串(输C,M命令的那个)一定要开成全局变量,否则WA的你飞起!(玄学)
3、最好改变一下写father的习惯,以后都用递归写!
链接一下另一篇题解银河英雄传说