[NOI2001] 食物链

  带权并查集。

  学习参考:https://agatelee.cn/2017/05/%E5%B8%A6%E6%9D%83%E5%B9%B6%E6%9F%A5%E9%9B%86/

  注意事项与可能产生的疑惑。

  1:节点的权直接代表与根节点的关系。

  2:路径压缩中可能出现节点权并不代表与当前根节点的关系,因为该节点是新合并的。

       且从某个节点到根节点的路径中,至多有一个点会存在这样的关系。

       且这个点是之前没有路径压缩过的点。

       该点的权代表与之前跟节点的关系,且该节点之前的根节点一定是f[x],即该节点的父节点。

       那么 w[x] 应为 ( w[x] + w[f[x]] ) % 3。

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int M=50000+10;
/****************************************************************/
int f[M],w[M];
int finds(int x) {
	if(f[x]!=x) {
		int tmp=f[x];
		f[x]=finds(f[x]);
		w[x]=(w[tmp]+w[x])%3; // 所有未路径压缩过的点的w[]值是与先前根节点的关系.
	}
	return f[x];
}
/****************************************************************/
int main() {
	freopen("eat.in","r",stdin);
	freopen("eat.out","w",stdout);
	int n,q,opt,x,y,fx,fy,ans=0;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) {
		f[i]=i,w[i]=0;
	}
	for(int i=1;i<=q;i++) {
		scanf("%d%d%d",&opt,&x,&y);
		if(x>n||y>n) {
			ans++; continue;
		}
		if(opt==1) {
			fx=finds(x);
			fy=finds(y);
			if(fx==fy) {
				if(w[x]!=w[y]) ans++;
				continue;
			}
			f[fx]=fy;
			w[fx]=(w[y]-w[x]+3)%3; // w[x]+w[fx]=w[y].
		} else {
			fx=finds(x);
			fy=finds(y);
			if(fx==fy) {
				if(w[x]!=(w[y]+1)%3) ans++;
				continue;
			}
			f[fx]=fy;
			w[fx]=(w[y]-w[x]+4)%3; // w[x]+w[fx]=w[y]+1.
		}
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/qjs12/p/8888388.html
今日推荐