slojP2577. 食物链
题目大意
说实话,我做对了之后都还是有点懵
语文不好都这么招歧视了吗,我太难了
三类动物 A,B,C,A 吃 B,B 吃 C,C 吃 A。给出若干关系,判断第几个关系是错误的。(貌似不太准确)
很显然, 对假话条件 2、3 自己去原题目中看的处理十分简单,只要在读入数据时作两个条件判断即可解决
题目的主要任务在于处理条件1
从表面上看, 条件1的处理似乎也没有什么难度:
一个动物无非就是 A,B,C 三类
而 A,B,C 之 间的食物链关系是一对一单向环形的
也就是说如果已知动物 X 所属种类和 X、Y 之间的食物链关系
就一定可以确定出动物 Y 的种类
同时某个动物具体属于哪一类并不影响本题的结果
而只要求它与其他动物关系的相对位置正确即可
于是, 我们有一个大胆的思路
不妨开3个数组A,B,C, 分别记录着三种类的成员
首先假设第一句有效话中的动物X为A类,将其放入数组A
倘若Y与X同类, 则把Y也放入A;
若Y被X吃,则将Y放入B,如此反复操作所有的有效话,就可以确定每个动物的种类,并容易统计出假话的个数。
问题似乎已经圆满地解决了也只是似乎,顺带说一句,上面那么一串思路我也没写不知道实际行不行,读者可自行尝试
但是,上面的这个算法存在着重大的错误
对于一个未知属性的生物我们都采取的是定义为 A 类型,这样子显然是有问题的。但有概率能解决这道题
这个算法只能当每一句话都可直接与此前已知的食物链建立明确关系的时候才能使用。
明确了上面这个关系,我们就不难从刚才的算法扩展出另一种算法:
对于目前关系未知的动物X,我们为他新开辟一条食物链A2,B2,C2
显然,在这个新的组中,动物X所在的种类也是随意的,
于是假设它在A2组,这样,所有与X的关系就可用与算法1同样的方式加入这个组中,
而这个组与原先的组 A1,B1,C1 的关系是不确定的。
如此反复,我们也可以得到组 3、组 4、组 5……,
一旦有一句话牵涉到某两个组的成员之间的食物链关系,
我们就依据一定的换算规则将这两个组合并起来,以保证关系网的完整性。
通过上面的分析,并查集在本题中的运用已经呼之欲出。
一个集合有三类的元素,合并集合的时候,需要对三类元素进行合并。
当然,肯定没几个人真的在代码中写出三个类别
而是会将元素之间的关系采用数字表示,
在这里,我用0表示与父亲元素同族,1表示吃父亲元素,2表示被父亲元素吃
可以以数学的方式在路径压缩时快捷的改掉关系
#include<bits/stdc++.h> using namespace std; int fa[50010],d[50010],ans,n,m; int find_(int x){ if(x!=fa[x]){ int t = fa[x]; fa[x] = find_(fa[x]); d[x] = (d[x]+d[t])%3; } return fa[x]; } void merge(int x,int y,int tmp){ int rootx = find_(x); int rooty = find_(y); if(rootx==rooty){ if((tmp-1)!=(d[x]-d[y]+3)%3) ans++; }else{ fa[rootx] = rooty; d[rootx] = (d[y]-d[x]+tmp-1)%3; } } int main(){ scanf("%d%d",&n,&m); for(int i = 1;i<=n;i++) fa[i] = i; for(int i = 1;i<=m;i++){ int tmp,x,y; scanf("%d%d%d",&tmp,&x,&y); if(x>n||y>n||(tmp==2&&x==y)) ans++; else merge(x,y,tmp); } printf("%d",ans); return 0; }
标签:关系,P2024,洛谷,食物链,int,fa,动物,NOI2001,tmp From: https://www.cnblogs.com/cztq/p/17002491.html