zoj 3789 并查集

浙大月赛的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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值