今天被队友鄙视了一下,决心复习
1、POJ 食物链
http://poj.org/problem?id=1182 “传送门”
思路:
A吃B , A->B 就让 relation[A] = relation[B] + 1, 然后模3 。 这么做当 B->C C->A的时候不会有问题吗? 不会, 仔细分析 并查集的结构是这样的
假设给出 A1->A , B1->A , C1->B1, D1->C1,
A
//A1// B1///C1///D1
并查集到最后变成所有的parent[ ] = A, 区分不同层次是用relation[],并查集的权值来区分。
还是上面的例子 A1->A , B1->A , C1->B1, D1->C1 则得到relation[]为
1 / 1 // 2 / 0(2+1 %3)这样的话就可以通过权值分析出关系了
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
const int maxn = 50050;
int parent[maxn], relation[maxn];
void init(int len)
{
for(int i=0; i<=len; i++)
{
parent[i] = i;
relation[i] = 0;
}
}
int root(int x)
{
if(parent[x] == x)
return x;
int temp = parent[x]; // 操作之前的祖先
parent[x] = root(parent[x]); // 操作之后 真正祖先
relation[x] = (relation[x] + relation[temp])%3; // x - >操作前祖先 + 操作前祖先->操作后祖先
return parent[x];
}
// a b root(a) , root(b) , distance
void merg(int a, int b, int x,int y, int d)
{
// a + d -- b
// 初始化 意思是a 为 根
// 权值就是当前点到 根的距离, 那么根的权值 小
int cnta = (d + relation[a] - relation[b] + 3) % 3;
parent[y] = x;
relation[y] = cnta;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
init(n);
int ans = 0;
while(m--)
{
int d,a,b;
scanf("%d%d%d",&d,&a,&b);
if(a>n || b>n || (d == 2&&a == b))
{
ans++;
continue;
}
if(d == 1)
{
int x = root(a), y = root(b);
if(x == y)
{
if(relation[a] != relation[b])
ans++;
}
else // 两只同类,如果之前未确立关系,那么应该要确立关系 而不是归为说假话
{
merg(a,b,x,y,0);
}
}
else
{
int x = root(a), y = root(b);
if(x == y) // 一棵树上的
{
// 两种类型的关系都不符合
if(relation[a] != relation[b] + 1 && relation[b] != relation[a] + 2)
ans++;
}
else
merg(b,a,y,x,1);
}
}
printf("%d",ans);
return 0;
}