食物链 (带权并查集)

食物链
Time Limit: 1000MS  Memory Limit: 10000K
Total Submissions: 37164  Accepted: 10811

Description

动物王国中有三类动物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。 
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
1) 当前的话与前面的某些真的话冲突,就是假话; 
2) 当前的话中X或Y比N大,就是假话; 
3) 当前的话表示X吃X,就是假话。 
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 

Input

第一行是两个整数N和K,以一个空格分隔。 
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 
若D=1,则表示X和Y是同类。 
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input

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

Sample Output

3

   

   思路:

   带权并查集。0标记为同类,2表示吃父亲节点,1表示被父亲节点吃。

   

   AC:

#include<stdio.h>
#define max 50000+5
int root[max],re[max];

int find(int a)
{
	if(root[a]==a) return a;
	int r=find(root[a]);
	re[a]=(re[a]+re[root[a]])%3;
	root[a]=r;
	return r;
}

int main()
{
    int n,m;
    int ans=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
      root[i]=i,re[i]=0;
    while(m--)
    {
    	int w,a,b;
    	int fa,fb;
    	scanf("%d%d%d",&w,&a,&b);
    	fa=find(a);
    	fb=find(b);
    	if(a>n||b>n)
    	{
    		ans++;
    		continue;
    	}
    	if(w==2&&a==b)
    	{
    		ans++;
    		continue;
    	}
    	if(fa==fb)
    	{
    		if(w==1)
    		{
    			if((re[a]-re[b]+3)%3!=0) 
    			{
    				ans++;
    				continue;
    			}
    		}
//题目给的1代表的是同类,而权里面0才是代表同类
    		else
    		{
    			if((re[a]-re[b]+3)%3!=2)
    			{
    				ans++;
    				continue;
    			}
    		}
    	}
    	else
    	{
    		root[fa]=fb;
    		if(w==1)   re[fa]=(0+re[b]-re[a]+3)%3;
    		else       re[fa]=(2+re[b]-re[a]+3)%3;
//这里是+0或者+2,不是+w,因为同类输入1的时候是+0的
    	}
    }
    printf("%d\n",ans);
	return 0;
}

    总结:

    1.注意自己规定权的意义和题目给出数字所代表意义的不同;

    2.有3种情况就+3,%3。所以规定有N种情况就+N,%N。最好从0开始来规定分类情况。

<think>好的,用户想查询与NOIP相关的食物链模拟或算法问题,特别是代码实现。首先,我需要回忆一下NOIP中涉及食物链的题目。记得NOI2001有一道食物链的问题,可能经常被选作NOIP的练习题或类似题目。用户提到的引用中有几个是关于食物链的,尤其是并查集的应用,比如引用2、3、4。所以应该围绕种类并查集(也叫扩展域并查集)来解答。 用户需要的是算法解析和代码实现。首先得解释问题的大意:动物分为A、B、C三类,构成环形食物链,A吃B,B吃C,C吃A。需要根据给定的陈述判断假话的数量。假话的条件包括X或Y超过范围、X吃X,或者与之前正确的陈述矛盾。 接下来,算法核心是种类并查集。通常的并查集只能处理同类关系,但这里需要处理捕食和被捕食的关系。根据引用3,将每个动物x分成三个域:同类域x,捕食域x+n,天敌域x+2n。这样,当处理两个动物的关系时,可以通过合并不同的域来维护他们之间的捕食或同类关系。 例如,当x和y是同类时,需要合并x和y,x+n和y+n,x+2n和y+2n。这样他们的捕食域和天敌域也保持一致。当x吃y时,需要合并x的捕食域和y的同类域,同时处理其他对应的域,确保环形关系正确。 代码部分需要实现并查集的查找和合并操作。引用2中的代码展示了如何初始化三个域,以及处理两种关系(同类和捕食)。需要处理三种假话情况,并在每次输入时检查是否冲突。 在编写代码时,需要注意路径压缩和按秩合并,这是并查集的常规优化。然后,对于每个陈述,分情况讨论,检查现有的并查集结构是否冲突,冲突则视为假话,计数加一。 用户可能对如何扩展并查集以及具体的合并逻辑有疑问,需要详细解释每个步骤的意义,比如为什么合并三个域,如何通过查询不同域来判断关系是否矛盾。此外,代码中的细节,比如数组的大小(3*n),以及如何通过查找不同域的关系来验证陈述的正确性,都需要解释清楚。 可能生成的相关问题包括种类并查集的其他应用,如何优化并查集的性能,或者这道题目的其他解法,比如带权并查集。但根据引用中的内容,扩展域并查集是主要的解决方法,应该围绕这个展开。 最后,确保代码部分正确使用语法,比如在Python中使用类实现并查集,或者C++中的数组处理。根据用户提供的引用2中的代码是C语言,但用户可能需要Python实现?不过原题通常用C++,但用户的问题没有限定语言。不过用户提供的引用中有C代码,可能更倾向于C风格,但需要确认。不过用户的问题提到NOIP,通常允许C++,所以代码示例可能需要用C++或者Python,但根据之前的回答示例中的代码是Python,这里可能需要用Python实现。不过原题的标准解法通常用扩展域并查集,可能需要用数组实现,因此代码结构可能类似于引用2中的C代码,但需要转换为Python或其他语言? 不过用户的问题并没有指定语言,所以可能需要用伪代码或者解释逻辑,但根据用户提供的示例中的代码是Python,所以可能更倾向于Python实现。不过原题可能更常见的是C++代码,但用户可能需要Python示例。需要根据情况调整。或者,可以给出算法框架,用伪代码描述,但用户可能需要实际可运行的代码。 综上,回答的结构应该包括问题描述、算法解析(种类并查集)、代码实现(可能用Python或C++,根据引用中的示例是C,但用户示例中的代码是Python,可能需要折中)、以及相关问题。同时需要确保使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$。</think>### NOIP食物链问题解析与代码实现 食物链问题是NOIP/NOI中经典的种类并查集应用题,要求根据动物间的捕食关系判断假话数量。问题核心是将每个动物$x$划分为三个逻辑域: - **同类域**:$x$(范围$1-n$) - **捕食域**:$x+n$(范围$n+1-2n$) - **天敌域**:$x+2n$(范围$2n+1-3n$) #### 算法步骤 1. **初始化**:每个动物的三个域各自独立 2. **关系判定**: - **同类关系**:合并$x,y$的同类域、捕食域、天敌域 - **捕食关系**:合并$x$的捕食域与$y$的同类域,同时维护环形关系 3. **冲突检测**:通过域间关系验证当前语句是否矛盾 #### 代码实现(Python) ```python class UnionFind: def __init__(self, size): self.parent = [i for i in range(3 * size + 1)] # 三个域 self.rank = [0] * (3 * size + 1) def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): fx = self.find(x) fy = self.find(y) if fx == fy: return if self.rank[fx] < self.rank[fy]: self.parent[fx] = fy else: self.parent[fy] = fx if self.rank[fx] == self.rank[fy]: self.rank[fx] += 1 def main(): n, k = map(int, input().split()) uf = UnionFind(n) false_count = 0 for _ in range(k): d, x, y = map(int, input().split()) if x > n or y > n or (d == 2 and x == y): false_count += 1 continue if d == 1: # 声明x与y同类 if uf.find(x) == uf.find(y + n) or uf.find(x + n) == uf.find(y): false_count += 1 else: uf.union(x, y) uf.union(x + n, y + n) uf.union(x + 2*n, y + 2*n) else: # 声明x吃y if uf.find(x) == uf.find(y) or uf.find(x) == uf.find(y + n): false_count += 1 else: uf.union(x + n, y) uf.union(x + 2*n, y + n) uf.union(x, y + 2*n) print(false_count) if __name__ == "__main__": main() ``` #### 关键逻辑说明 1. **同类判定**:若$x$的同类域与$y$的捕食域连通,说明存在矛盾[^3] 2. **捕食判定**:若$x$的同类域与$y$的同类域或捕食域连通,说明存在矛盾[^4] 3. **域合并**:通过三次合并操作维护环形食物链关系(A→B→C→A)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值