加权并查集入门习题。
传送门http://poj.org/problem?id=1182
下面来记录一下做法:
并查集的作用是询问两个对象时候在同一集合以及将两个非空不相交集合合并。
本题涉及两点之间是否能够两边,这样就可以生成多个集合。
而d=1 ,d=2则相当于将两个集合检索,若合法则合并,否则则将ans++。
但在检索时,我们发现采用一般的并查集无法实现(开三个除外),因为无法简单的利用两点是否在同一集合而判断是否可行。
故想到将其附加信息,而加权并查集的信息一般处理为儿子对父节点的关系或父节点对儿子的关系。
本题设rec[i]为i节点对fa[i]的关系。
如果通过a->b ,b->c能求出a->c,那么本题就变得十分简单。
通过枚举可以发现
令0表示同类,1表示被父节点吃,2表示吃父节点。
这样在求a->c 时,就可以用(rec[i]+rec[fa[i]])%3来表示。
下面上代码:
#include "iostream"
#include "cstdio"
#include "cstring"
#include "cctype"
using namespace std;
const int maxn = ;
int n ,k ,fa[maxn] ,rec[maxn];
void read(int &x)
{
x = ;
char c = getchar();
while(!isdigit(c))
c = getchar();
while(isdigit(c))
{
x = (x<<) + (x<<) + c - '';
c = getchar();
}
return ;
} int find_(int x)
{
if(x == fa[x]) return x;
int temp = fa[x];
fa[x] = find_(fa[x]);
rec[x] = (rec[temp]+rec[x])%;
return fa[x];
} int Union(int x ,int y ,int d)
{
int f1 = find_(x) ,f2 = find_(y);
if(f1 == f2)
{
if(d == )
{
if(rec[x] == rec[y])
return ;
else return ;
}
if(d == && rec[x] != (rec[y] + )%)
return ;
return ;
}
else
{
fa[f2] = f1;
rec[f2] = (rec[x] + (d-) + (-rec[y])) % ;
}
return ;
} int main()
{
read(n); read(k);
int num1 ,num2 ,num3 ,ans = ;
for(int i=; i<=n; i++) fa[i] = i ,rec[i] = ;
for(int i=; i<=k; i++)
{
read(num1);
read(num2);
read(num3);
if(num2>n || num3>n)
{
++ans;
continue;
}
if(num1 == && num2 == num3)
{
++ans;
continue;
}
ans += Union(num2 ,num3 ,num1);
}
printf("%d" ,ans);
}