浙大月赛的B题,搞了一天,终于搞懂了,第一次敲并查集
注意注释地方的代码,有很多人的代码是dis[a] = dis[b] + 1
这样是不对的,那些程序都有一种数据是过不了的:
100 10000
L 1 2
L 2 3
L 4 5
L 5 6
L 6 7
L 7 8
L 8 9
L 1 5
Q 2 7
不信可以试试,但是那么多人把代码贴出来肯定是能AC的
这种情况就需要按注释那样写
为什么呢
比方说上面这组数据最后分组时
1 2 3
4 5 6 7 8 9
最后要把1 和5连接起来
然后就要更新距离,怎么更新呢,好多人是更新1 的距离,这样做显然过不了上面这组数据,怎么办呢?
因为题目只要求是否方向相同,也就是距离根节点的奇偶性,那么这个时候我们让第一组的根节点3的距离变为到第二组的根节点的距离( 3 2 1 5 6 7 8 9),这样的话,你会认为最后更新了2之后 2 的距离就是 2 3 2 1 5 6 7 8 9,多出了 3 2 ,但是我们提到只需要注意奇偶性,又因为多出来的肯定是偶数(到原根节点再回来),所以这里多出来的是无关紧要的,但是却能使我们的操作更方便(如果不这么做的话,很难这么容易的实现link函数)
弄了一天终于弄懂了,好开心!
AC代码如下:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 600000;
int dis[MAXN], hash[MAXN], ft[MAXN], sz[MAXN];
int N, M, top;
int find( int a ){
if( a != ft[a] ){
int father = find( ft[a] );
dis[a] += dis[ft[a]];
ft[a] = father;
return father;
}
return a;
}
void link( int a, int b ){
int fa = find( a );
int fb = find( b );
if( fa != fb ){
ft[fa] = fb;
dis[fa] = dis[a] + dis[b] + 1;//合并时应根据两树上的节点距离来确定两树的根节点距离
sz[fb] += sz[fa];
}
}
int main(){
while( scanf( "%d%d", &N, &M ) != EOF ){
memset( dis, 0, sizeof( dis ) );
for( int i = 1; i <= N; i++ ){
hash[i] = i;
ft[i] = i;
sz[i] = 1;
}
top = N + 1;
for( int i = 0; i < M; i++ ){
char s[10];
int temp1, temp2;
scanf( "%s", s );
if( s[0] == 'L' ){
scanf( "%d%d", &temp1, &temp2 );
link( hash[temp1], hash[temp2] );
}else if( s[0] == 'Q' ){
scanf( "%d%d", &temp1, &temp2 );
int fa = find( hash[temp1] );
int fb = find( hash[temp2] );
if( fa == fb ){
if( abs( dis[hash[temp1]] - dis[hash[temp2]] ) % 2 == 0 ){
printf( "Same\n" );
}else{
printf( "Different\n" );
}
}else{
printf( "Unknown\n" );
}
}else if( s[0] == 'S' ){
scanf( "%d", &temp1 );
find( hash[temp1] );
printf( "%d\n", sz[ft[hash[temp1]]] );
}else{
scanf( "%d", &temp1 );
sz[top] = 1;
dis[top] = 0;
find( hash[temp1] );
sz[ft[hash[temp1]]]--;
hash[temp1] = top++;
ft[hash[temp1]] = hash[temp1];
}
}
}
return 0;
}