食物链(NOI2001,洛谷P2024)

题目描述

动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,B吃 C,C 吃 A。

现有 N 个动物,以 1- N 编号。每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。

有人用两种说法对这 N 个动物所构成的食物链关系进行描述:

  • 第一种说法是 1 X Y,表示 X 和 Y是同类。
  • 第二种说法是 2 X Y,表示 X 吃 Y。

此人对 NN 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

  • 当前的话与前面的某些真的话冲突,就是假话;
  • 当前的话中 X 或 Y 比 N大,就是假话;
  • 当前的话表示 X 吃 X,就是假话。

你的任务是根据给定的 N和 K 句话,输出假话的总数。

输入格式

第一行两个整数,N,K,表示有 N 个动物,K 句话。

第二行开始每行一句话(按照题目要求,见样例)

输出格式

一行,一个整数,表示假话的总数。

输入输出样例

输入 #1

100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5

输出 #1

3

说明/提示

对于全部数据,1<=N<=5*10^4  1<= K<=10^5.

题目分析

1.算法:N只动物,3个种类,直接能想到并查集;

   但食物链中又有3个种类之间的关系,

   因此,并查集中还应设有虚点,一种动物会有吃它们的动物与它们吃的动物,共3种,

    所以并查集的fa数组所开空间为3*n

(fa[1]~fa[n] :fa[i]表示编号为i的动物所处集合的代表元,fa[n+1]~fa[2*n]表示吃编号为i-n的动物所处集合的代表元,fa[2*n+1]~fa[3*n]表示编号为i-2*n的动物所处集合的代表元)

并查集初始化:

for(int i=1;i<=3*n;i++) 
	{
		f[i]=i;
		h[i]=0;
	}

基础的find与unio(n)操作:

int find(int k)
{
	if(f[k]==k) return k;
	else return f[k]=find(f[k]);
}
void unio(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(h[xx]==h[yy]) 
	{
		h[xx]+=1;
		f[yy]=xx;
	}
	else
	{
		if(h[xx]>h[yy]) f[yy]=xx;
		else f[xx]=yy;
	}
}

主程序:

由题意可知

谎话有

1.x或y超出范围的:

int opt,x,y;
cin>>opt>>x>>y;
if(x>n||y>n||x<0||y<0) 
{
	ans++;
	continue;
}

2.操作1:

        x与y是同类但已知x吃y或y吃x;

        翻译:x与吃y的东西是同类或y与吃x的东西是同类;

        根据前文对f数组的定义可知,就是:

        

if(opt==1)
{
	if(find(x)==find(y+n)||find(y)==find(x+n))
	{
		ans++;
		continue;
	}
}

否则,把x与y及它们的天敌与它们的食物所在集合合并:

unio(x,y);
unio(x+n,y+n);
unio(x+2*n,y+2*n);

操作2:

x与y是同一样东西或它们是同一种动物或y吃x:

if(opt==2)
{
	if(x==y||find(x)==find(y)||find(y)==find(x+n)) 
	{
		ans++;
		continue;
	}
}

否则由于它们的环状关系:

unio(x,y+n);
unio(x+n,y+2*n);
unio(y,x+2*n);

完整代码如下:

#include
using namespace std;
int n,m,f[1000001],h[1000001];
int find(int k)
{
	if(f[k]==k) return k;
	else return f[k]=find(f[k]);
}
void unio(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(h[xx]==h[yy]) 
	{
		h[xx]+=1;
		f[yy]=xx;
	}
	else
	{
		if(h[xx]>h[yy]) f[yy]=xx;
		else f[xx]=yy;
	}
}
int main()
{
	cin>>n>>m;
	int ans=0;
	for(int i=1;i<=3*n;i++) 
	{
		f[i]=i;
		h[i]=0;
	}
	for(int i=1;i<=m;i++)
	{
		int opt,x,y;
		cin>>opt>>x>>y;
		if(x>n||y>n||x<0||y<0) 
		{
			ans++;
			continue;
		}
		if(opt==1)
		{
			if(find(x)==find(y+n)||find(y)==find(x+n))
			{
				ans++;
				continue;
			}
			unio(x,y);
			unio(x+n,y+n);
			unio(x+2*n,y+2*n);
		}
		if(opt==2)
		{
			if(x==y||find(x)==find(y)||find(y)==find(x+n)) 
			{
				ans++;
				continue;
			}
			unio(x,y+n);
			unio(x+n,y+2*n);
			unio(y,x+2*n);
		}
	}
	cout<

你可能感兴趣的:(并查集,算法,数据结构)