这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战.
带权并查集的每个节点除了记录fa外,还记录一个val,表示与根节点的差值。 find过程中,需要更新val,加上原来父亲的val,注意原来的父亲需要存储在临时变量中,不然会被覆盖。 join过程中,依旧是对两个根建立父子关系,设新关系为a-b=s,且令fa[ra]=rb,那么val[ra] = v-val[a]+val[b].
struct UFS
{
int fa[M], val[M]; //val表示比根高多少分
void init(int n)
{
for(int i=1; i<=n; ++i)
fa[i] = i, val[i] = 0;
}
int find(int n)
{
if(fa[n]==n) return n;
int tmp = fa[n];
fa[n] = find(tmp);
val[n] += val[tmp];
return fa[n];
}
// a-b=v
// (a-val[a]) - (b-val[b]) = v-val[a]+val[b];
void join(int a, int b, int v) //a比b高v
{
int ra=find(a), rb=find(b);
fa[ra] = rb;
val[ra] = v-val[a]+val[b];
}
}ufs;
hihocoder #1515 : 分数调查
/* LittleFall : Hello! */
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n = read(), m = read(), q = read();
ufs.init(n);
for(int i=1; i<=m; ++i)
{
int x = read(), y = read(), s = read();
//printf("join %d %d %d\n",x,y,s );
ufs.join(x, y, s);
// for(int i=1; i<=n; ++i)
// {
// ufs.find(i);
// printf("%d %d\n",ufs.fa[i],ufs.val[i] );
// }
}
for(int i=1; i<=q; ++i)
{
int x = read(), y = read();
if(ufs.find(x)==ufs.find(y))
{
printf("%d\n",ufs.val[x]-ufs.val[y] );
}
else
{
printf("-1\n");
}
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
考虑带权并查集,此时的权值表示与根节点的关系:0表示同类,1表示吃根节点,2表示被根节点吃。
那么:当前节点到根节点的关系 = (当前节点到父节点的关系 + 父节点到根节点的关系) % 3
节点A到节点B的关系 = (A到根的关系 - B到根的关系)% 3,前提是A和B在一个连通块中。
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n = read(), q = read(), ans = 0;
ufs.init(n);
while(q--)
{
int op = read(), x = read(), y = read();
if(x>n || y>n) ++ans;
else if(op==1)
{
if(ufs.find(x)==ufs.find(y) && ufs.val[x]!=ufs.val[y])
++ans;
else
ufs.join(x, y, 0);
}
else
{
if(ufs.find(x)==ufs.find(y) && (ufs.val[x]-ufs.val[y]+3)%3!=1)
++ans;
else
ufs.join(x, y, 1);
}
}
printf("%d\n",ans );
return 0;
}
本文也发表于我的 csdn 博客中。