【NOI2001T1】食物链-并查集

测试地址

题目大意:一个地方的动物分A,B,C三类,A吃B,B吃C,C吃A,两种动物之间要么是同类,要么就有吃与被吃的关系。按顺序给定K句话,描述的是某两种动物是同类或某种动物吃某种动物。求这些话中假话的数量(假话的定义在原题中有)。

做法:用f[i]表示i号动物所属集合的代表动物,r[i]表示i号动物与它所属集合代表动物之间的关系,为0时代表它们是同类,为1时代表i号动物吃它所属集合的代表动物,为2时反之。由于动物之间的关系刚开始是不确定的,因此我们用并查集来记录哪些动物之间的关系是可以确定的。每读入一个关系,如果关系涉及的两种动物不属于同一个集合,则表示它们之间的关系仍不能确定,则认定这句话为真话,合并两个集合。如果它们属于同一个集合,则表示它们之间的关系已经可以确定,则先求出它们之间的关系,再将它与描述中的关系比较,判断是否为假话。关键就在于怎么维护这个关系。这里主要是合并和求某两种动物之间关系的问题:合并时,如有两种动物x,y,关系为d,因为x与f[x]关系为r[x],y与f[y]关系为r[y],得出f[x]和f[y]之间关系为(r[y]+d-r[x]+3) mod 3,括号里加上3是为了防止减的结果为负数,然后再像普通并查集一样合并即可。求两种动物x,y之间关系,可以用上面的推论方法得出它们之间的关系为(r[x]-r[y]+3) mod 3。路径压缩中维护r的方法与上面的推论方法类似,不再赘述。至此,我们已经可以解决这个问题了。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
long n,k,ans=0,f[50010]={0},r[50010]={0};

long find(long x)
{
  long t;
  if (f[x]==x) return x;
  t=f[x];
  f[x]=find(f[x]);
  r[x]=(r[t]+r[x])%3;
  return f[x];
}

void merge(long x,long y,long len)
{
  long fx=find(x),fy=find(y);
  f[fx]=fy;
  r[fx]=(r[y]-r[x]+3+len)%3;
}

int main()
{
  scanf("%ld %ld
",&n,&k);
  
  for(int i=1;i<=n;i++)
    f[i]=i;
  
  for(int i=1;i<=k;i++)
  {
    long d,x,y;
    scanf("%ld %ld %ld
",&d,&x,&y);
    if (x>n||y>n||(d==2&&x==y)) ans++;
	else
	{
	  long fx=find(x),fy=find(y);
	  if (fx==fy)
	  {
	    if ((r[x]-r[y]+3)%3!=d-1) ans++;
	  }
	  else merge(x,y,d-1);
	}
  }
  
  printf("%ld",ans);
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793934.html