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 三种动物有三种状态,所以把数组开到原来的三倍即可。