NKOJ 1209 并查集【NOI2001 Day1 T3】食物链

NKOJ 1209 并查集【NOI2001 Day1 T3】食物链

思路:带权/种类并查集

方法一

实现方法

  • 用带权并查集带的权值是边权,不是点权,用来表示两点间的关系,但为了方便记录还是用点权,每个点记录到根节点的权值。
  • getf 函数中注意更新是到根节点之间的权值,用 \(val_x=(val_x+val_{fa_x}) \bmod 3\) 来更新。
  • merge 函数中:
    • 如果两个点在同一个集合,就判断是否有矛顿。(通过 \((val_x-val_y+3) \bmod 3\) 来调取 \(x\)\(y\) 之间的关系)
    • 如果不在,就合并两个点所在的集合,方法是分别调取和跟节点之间的关系相加之后再加上 \(x\)\(y\) 的关系得到 \(val_{f_x}=(val_y-val_x+relation-1) \bmod 3\)

代码

#include<cstdio>
using namespace std;
int n,m,ans=0;
int rel[500005],f[500005];
int getf(int x){
	if(x!=f[x]){
		int t=f[x];
		f[x]=getf(f[x]);
		rel[x]=(rel[x]+rel[t])%3;
	}
	return f[x];
}
void merge(int x,int y,int r){
	int fx=getf(x),fy=getf(y);
	if(fx==fy){
		if(((rel[x]-rel[y]+3)%3)!=(r-1)) ans++;
	}
	else{
		f[fx]=fy;
		rel[fx]=(rel[y]-rel[x]+r-1)%3;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) f[i]=i;
	while(m--){
		int x,y,d;
		scanf("%d%d%d",&d,&x,&y);
		if((x==y&&d==2)||y>n||x>n) ans++;
		else merge(x,y,d);
	}
	printf("%d",ans);

	return 0;
}

方法二(未实现)

实现方法

  • 普通种类并查集维护朋友和敌人两种关系,在本题中 A、B、C 三种动物有三种状态,所以把数组开到原来的三倍即可。
posted @ 2024-12-14 10:01  hsr_ray  阅读(24)  评论(0)    收藏  举报