题目链接:https://vjudge.net/problem/POJ-1182
错误代码
用并查集表示是同类,vector保存关系。查询时用的是根节点的关系,由于合并时没有把子节点上的关系转移到根节点导致关系缺失。
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <cmath>
#include <map>
#include <list>
#include <stack>
using namespace std;
int pa[50005];
vector <int> g[50005];
int fnd(int x)
{
return x==pa[x]?x:pa[x]=fnd(pa[x]);
}
int check(int a,int b)
{
int la=g[a].size(),lb=g[b].size();
for(int i=0; i<la; i++)
{
if(g[a][i]==b)
return 1;
}
for(int i=0; i<lb; i++)
{
if(g[b][i]==a)
return 2;
}
return 0;
}
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
{
pa[i]=i;
}
int cnt=0;
for(int i=0; i<k; i++)
{
int d,x,y;
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n)
{
cnt++;
continue;
}
int a=fnd(x),b=fnd(y);
if(d==1)
{
if(check(a,b)>0)
cnt++;
else
{
pa[a]=b;
}
}
else
{
if(a==b)
cnt++;
else if(check(a,b)==2)
cnt++;
else
g[a].push_back(b);
}
}
printf("%d\n",cnt);
return 0;
}
种类并查集
转自:https://www.cnblogs.com/fly-white/p/10092762.html
同一个集合表示同一种动物。每一种动物有3个节点 : x,x+n,x+2 * n,x表示自己,x+n表示其天敌,x+2 * n表示天敌的天敌。特别注意x是x+2 * n的天敌。每次合并时都要维护三个点,这样关系就不会缺失了。
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <cmath>
#include <map>
#include <list>
#include <stack>
using namespace std;
int pa[150005];
int fnd(int x)
{
return x==pa[x]?x:pa[x]=fnd(pa[x]);
}
void merge1(int x,int y)
{
x=fnd(x),y=fnd(y);
pa[x]=y;
}
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1; i<=3*n; i++)
{
pa[i]=i;
}
int cnt=0;
for(int i=0; i<k; i++)
{
int d,x,y;
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n)
{
cnt++;
continue;
}
if(d==1)
{
if(fnd(x)==fnd(y+n)||fnd(x)==fnd(y+2*n))
{
cnt++;
continue;
}
else
{
merge1(x,y);
merge1(x+n,y+n);
merge1(x+2*n,y+2*n);
}
}
else
{
if(fnd(x)==fnd(y)||fnd(x)==fnd(y+2*n))
{
cnt++;
continue;
}
else
{
merge1(x,y+n);
merge1(x+n,y+2*n);
merge1(x+2*n,y);
}
}
}
printf("%d\n",cnt);
return 0;
}
带权并查集
转自:https://blog.csdn.net/niushuai666/article/details/6981689
只要两点有关系,无论是同类还是吃与被吃,都合并到一个集合中。 定义p[a].rela表示a的pre相对于节点a的偏移量: 0 同类 1 根节点吃a 2 a吃根节点 op-1即关系。
设px为x的父节点,ppx为px的父节点,已知px与x的rela和px与ppx的rela,就可以得到ppx与x的rela(一个个列出来即可得到规律),即ppx相对于x的rela =(px相对于x的rela+ppx相对于px的rela)%3。假设有一条链的集合,1-2-3-4-5,5为根节点,更新时,会先更新4和5的rela,然后用4作为px更新3,在用3作为px更新2……。
合并时,因为节点的rela都是相对于根节点的,因此也要用根节点进行合并。 用类似于向量的思想 ,例如1->2,3->4,13为根节点,现在给出2与4的关系,可以把3号节点作为1的子节点进行合并,那么1对3的rela由1->2+2->4+4->3得到,即(p[2].rela+(op-1)-p[4].rela+3)%3,+3防止出现负数,4->3的rela就是3对4的rela的相反数。
对于题目,如果输入的两点没有关系,可以直接合并。如果有关系,需要判断。1.如果根节点对两点的rela不相等,那么它们一定不是同类 2.如果x可以吃y,设根节点为z,那么(x->z+z->y)%3一定等于op-1即1 一定要注意方向
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
struct node
{
int pre,rela;
} p[50010];
int find(int x)
{
if(x==p[x].pre)
return x;
int t=p[x].pre;
p[x].pre=find(p[x].pre);
p[x].rela=(p[x].rela+p[t].rela)%3;
return p[x].pre;
}
int main()
{
int n,k;
int op,a,b,root1,root2;
int sum=0;
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
{
p[i].pre=i;
p[i].rela=0;
}
for(int i=0; i<k; i++)
{
scanf("%d%d%d",&op,&a,&b);
if(a>n||b>n)
{
sum++;
continue;
}
if(op==2&&a==b)
{
sum++;
continue;
}
root1=find(a);
root2=find(b);
if(root1!=root2)//两节点没有关系,可以直接合并
{
p[root2].pre=root1;
p[root2].rela=(3+p[a].rela+(op-1)-p[b].rela)%3;
}
else//两节点有关系,需要判断
{
if(op==1&&p[a].rela!=p[b].rela)//两点相对于根节点的rela不相等 就一定不是同类
{
sum++;
continue;
}
if(op==2&&((3-p[a].rela+p[b].rela)%3!=op-1))
{
sum++;
continue;
}
}
}
printf("%d\n",sum);
return 0;
}