【并查集】(并查集的合并中 使用了启发式合并)数据分割 HDU - 6109 (相等关系:并查集 不等关系:set)

Key Point:
相等关系:可传递的,使用并查集进行检验
不等关系:不可传递,使用set进行检验
(如 X1 != X2 X1 != X3 但是X2 = X3 是可以成立的)


数据分割 HDU - 6109

小w来到百度之星的赛场上,准备开始实现一个程序自动分析系统。

这个程序接受一些形如xi=xj 或 xi≠xj
的相等/不等约束条件作为输入,判定是否可以通过给每个 w 赋适当的值,来满足这些条件。

输入包含多组数据。
然而粗心的小w不幸地把每组数据之间的分隔符删掉了。
他只知道每组数据都是不可满足的,且若把每组数据的最后一个约束条件去掉,则该组数据是可满足的。

请帮助他恢复这些分隔符。

Input
第1行:一个数字L,表示后面输入的总行数。

之后L行,每行包含三个整数,i,j,e,描述一个相等/不等的约束条件,若e=1,则该约束条件为xi=xj ,若e=0,则该约束条件为 xi≠xj 。

i,j,L≤100000

xi,xj≤L

Output

输出共T+1行。

第一行一个整数T,表示数据组数。

接下来T行的第i行,一个整数,表示第i组数据中的约束条件个数。

Sample Input
6
2 2 1
2 2 1
1 1 1
3 1 1
1 3 1
1 3 0

Sample Output
1
6

思路:
检验关系式是否矛盾,如果出现矛盾,就是一组数据的结束。

这里使用并查集 表示相等关系,用set表示不等关系。

并查集的合并中 使用了启发式合并。(其实和之前并查集一样,用了num,将num小的加大到num大的上面,按秩合并,但是我这里没有用也过了23333)

  • 需要注意的是:当x和y合并时,需要把x所在集合的代表元素fx的set 和 y所在集合的代表元素fy的set 合并,(而不是把x和y的set合并,因为我们在进行检验时,他们都到一个集合里了,那么不等关系也要共享,我们不可能把x的set给集合中的每一个元素都共享一遍,我们只需要把x的set给代表元素fx(即集合的根)即可,因为我们在check时,也使用的是代表元素fx,而不是x。)

  • 将x和y合并时,如果把fy作为新的根,那么只需要把fx的set加给fy就可以了,不需要吧fy的也加给fx,因为fa[fx]要变成fy,即fx的代表元素也换成了fy了。

  • 因此,Merge函数和Find函数都需要 加上set的部分。( Find 需要将儿子的set并给父节点。) (Merge就和上面说的一样。)

AC代码:

#include <iostream>
#include <set>
#include <iterator>

using namespace std;

const int maxn = 1e5 + 5;

int fa[maxn], ans[maxn];
int n;
set<int> s[maxn];

void Push(int x, int p)
{
    set<int>::iterator it;
    for(it = s[x].begin(); it != s[x].end(); it++)
        s[p].insert(*it);
}

int Find(int x)
{
    if(fa[x] == x)
        return x;
    Push(x, fa[x]);
    return fa[x] = Find(fa[x]);
}

void Merge(int x, int y)
{
    int fx = Find(x), fy = Find(y);
    if(fx == fy)
        return;
    Push(fx, fy);
    fa[x] = fy;
}

void Init()
{
    for(int i = 0; i <= n; i++)
    {
        fa[i] = i;
        s[i].clear();
    }
}

int main()
{
    scanf("%d", &n);
    Init();
    int u, v, e;
    int cnt = 0, t = 0;
    for(int i = 0; i < n; i++)
    {
        scanf("%d%d%d", &u, &v, &e);
        cnt++;
        int fx = Find(u);
        int fy = Find(v);
        if(e == 1)
        {
            if(s[fx].find(fy) == s[fx].end() && s[fy].find(fx) == s[fy].end())
            {
                Merge(fx, fy);
                continue;
            }
        }
        else
        {
            if(fx != fy)
            {
                s[fx].insert(fy);
                s[fy].insert(fx);
                continue;
            }
        }
        ans[++t] = cnt;
        cnt = 0;
        Init();
    }
    printf("%d\n", t);
    for(int i = 1; i <= t; i++)
        printf("%d\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值