这道题可以用带权并查集写,但是我觉得很难想到。。
听说是枚举归纳出权值之间的关系(这种权值关系的对应,就算在路径压缩的时候也是可以的)
书上的代码很好。,
但是我写的有点麻烦。。、
x 为x属于a集合, x+m 属于b集合, x+2*m为c集合。。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#include <queue>
/*这一道题可以用带权并查集写,但是我感觉让我写是很困难的。。貌似推出那个关系用向量的写法。
我觉得我还没达到那个水平。。
书上的解法就很好。
相当于思维。就是用三个并查集
*/
using namespace std;
const int maxn=150008;
int fa[maxn];
int find1(int a){
if(a==fa[a]) return a;
return fa[a]=find1(fa[a]);
}
void unite(int a,int b){
int x=find1(a);
int y=find1(b);
if(x!=y)
fa[x]=y;
}
void init(int m){
for(int i=1;i<=m*3;i++){
fa[i]=i;
}
}
bool same(int a,int b){
if(find1(a)==find1(b))
return true;
return false;
}
int main()
{ int m,n;
int a,b,c;
scanf("%d%d",&m,&n);
int ans=0;
init(m);
for(int i=0;i<n;i++){
scanf("%d%d%d",&c,&a,&b);
if(b>m||a>m)
{ //puts("!");
ans++;}
else if(c==1){
if(same(a,b+m)||same(a+m,b+2*m)||same(a+2*m,b)||same(b,a+m)||same(b+m,a+2*m)||same(b+2*m,a))
//其实呢,这6个,前三个任意取一个,后三个任意取一个就行。因为存在一个,其他俩也一定会存在(为啥??加的时候就是这样。。。)
{ //puts("?");
ans++;}
else{ unite(a,b);
unite(a+m,b+m);
unite(a+2*m,b+2*m);
}
}
else if(c==2){
if(same(a,b)||same(a+m,b+m)||same(a+2*m,b+2*m)||same(b,a+m)||same(b+m,a+2*m)||same(b+2*m,a))
//同理,一个是同类的关系,另一个反吃的关系
{
ans++;}
else{ unite(a,b+m);
unite(a+m,b+2*m);
unite(a+2*m,b);
}
}
}
printf("%d\n",ans);
return 0;
}
大佬的带权代码。。感觉很难想到啊qwq
#include <stdio.h>
#include <memory.h>
#define MAXN 50001
int father[MAXN],rank[MAXN];
void Init(int n)
{
int i;
for(i=1;i<=n;i++)
father[i]=i;
memset(rank,0,sizeof(rank));
}
int Find_Set(int x)
{
if(x!=father[x])
{
int fx=Find_Set(father[x]);
rank[x]=(rank[x]+rank[father[x]])%3; //注意 是rank[father[x]]而不是rank[fx]
father[x]=fx;
}
return father[x];
}
bool Union(int x,int y,int type)
{
int fx,fy;
fx=Find_Set(x);
fy=Find_Set(y);
if(fx==fy)
{
if((rank[y]-rank[x]+3)%3!=type)return true; //这个关系可以通过举例得出
else return false;
}
father[fy]=fx;
rank[fy]=(rank[x]-rank[y]+type+3)%3; // 与上式不同 需仔细归纳
return false;
}
int main()
{
freopen("in.txt","r",stdin);
int n,k;
int sum,i;
int d,x,y;
scanf("%d %d",&n,&k);
//cin>>n>>k;
Init(n);
sum=0;
for(i=0;i<k;i++)
{
scanf("%d %d %d",&d,&x,&y);
//cin>>d>>x>>y; //用cin会超时
if(x>n || y>n ||(x==y && d==2))
sum++;
else if(Union(x,y,d-1)) //传d-1 方便关系式的表达
sum++;
}
printf("%d\n",sum);
return 0;
}