# include<iostream> # include<cstdio> # include<cstdlib> using namespace std; typedef struct animals { int relation; int ustc; }animal; animal thing[50010]; int find(int i) { if (thing[i].ustc != i){ int temp; temp= find(thing[i].ustc); thing[i].relation = (thing[i].relation + thing[thing[i].ustc].relation) % 3; thing[i].ustc = temp; //cout << "i=" << i << endl; //cout << "temp=" << temp << endl; //printf("Animal %d s Parent is %d\n",i, thing[i].ustc); //cout << "thing["<<i<<"].relation=" << thing[i].relation << endl; } return thing[i].ustc; } //const int same = 0; //const int eated = 1; //const int eat = 2; int main() { int N, K; scanf("%d %d", &N, &K); int data = 0; for (int i = 1; i <= N; i++) { thing[i].ustc = i; thing[i].relation = 0; } for (int i = 0; i < K; i++) { int judge, parents, son; scanf("%d%d%d", &judge, &parents, &son); if ((parents>N) || (son > N)){ data++; //cout << "wrong answer 1" << ' ' << i << endl; continue; } else { int a, b; a = find(parents); b = find(son); //cout << "thing[parents].ustc=" << thing[parents].ustc << ' ' << "thing[parents].relation=" << thing[parents].relation << endl; //cout << " thing[son].ustc=" << thing[son].ustc << ' ' << " thing[son].relation= " << thing[son].relation << endl; if (a == b){ if (judge == 1){ if (thing[parents].relation % 3 == thing[son].relation % 3)continue; else { data++; //cout << "wrong answer 2" << ' ' << i<< endl; continue; } } else if (judge == 2){ if ((thing[son].relation - thing[parents].relation + 3) % 3 == 1)continue; else { data++; //cout << "wrong answer 3" <<' '<<i<< endl; continue; } } } else if (a != b){ thing[b].ustc = a; thing[b].relation = (3 - thing[son].relation + (judge - 1) + thing[parents].relation) % 3; } } } cout << data << endl; return 0; }
看了一些其他人的解题报告,思路自己整理了好久才终于从中挣脱出来,谢谢那位提供超详细解题报告的大神,整个题的思路多谢他的帮助,不过还是有一点不同想说一下,首先是节点关系的更新不一定要在根节点确定之后,在我的代码中之前一直保持先确定根节点再更新节点与根节点关系,具体的代码书写方案还是和个人的想法与代码结构有关。
附注(这是小逸的第一次写解题报告,有一些问题或者不够完善不清晰的地方还请各位大神提醒,小逸时刻准备着orz)
如网上大多数的大神所说,食物链这一题是一个带权值得并查集问题,题目的大体意思很简单,不断地创建一个动物与另一个动物的关系,然后排除与之前关系冲突的关系和动物代号超出范围的情况,最后输出这2种情况出现的总次数。
首先我们需要创建一个动物,这个动物根据需求,有2个属性:一个parent:父亲节点,和relation:他与其父亲节点的关系;
然后我们设定3个权值 0,1,2;
分别代表当前节点与根节点的关系是:与根节点为同一种族,根节点吃他,根节点被它吃;
到了这一步,很多人都会问为什么要将值设置成0,1,2;我只能说这样设定是有意义的,不是随便设的,至于具体为何我也不太清楚,到后面使用到同余定理,以及节点与节点之间的变换时,我们会发现这个值的确定会给计算带来很多方便。
我们是通过所给数据来不断的建立节点与节点之间的关系,这里我们需要处理一个问题,就是当结点变化后,之前结点上的子结点与新父亲结点的关系该如何确立;而这都是有规律可循的;我所使用的是和code4fun大神同样的穷举法,虽然感觉这方法总是有点笨,不过对于这种变换情况种类并不多的情况下,还是很有效的。
儿子->父亲 | 父亲->爷爷 | 儿子->爷爷 |
0 | 0 | 0 |
1 | 0 | 1 |
2 | 0 | 2 |
1 | 1 | 2 |
1 | 2 | 0 |
2 | 1 | 0 |
2 | 2 | 1 |
0 | 2 | 2 |
上面这一个关系推导出来之后其他的就好解决了。
我们解决这一题的思路是:
首先我们找出所给数据的2个的根节点,如果2只动物的根节点相同,那么直接通过判断他们与根结点的关系来判断他们之间的关系然后与数据进行比较;
如果2只动物的根节点不相同,那么说明这2只生物原来没有任何关系,那么这组数据就始终是正确的,然后我们需要将一只动物的根节点连接到另一只动物的根节点上;
以此来实现2个之间集合关系的确立,2个集合的根根节点变成了1个根节点。
先解决第一种情况:加入2只动物的根节点相同,此时如果第一个数据为1,则表示这2只动物是一样的生物,那么直接判断他们与根结点的关系是否相同即可;
如果第一个数据为2,则表示第一只生物对第2只生物有捕食关系,此时如何判断他们在集合中的关系呢?我们这里可以将第2只生物视作是根节点的一个根节点;
然后我们运用一次上面的公式:儿子->爷爷=(儿子->父亲 + 父亲->爷爷)%3;
在我们这里就是 动物1->动物2=(动物1->根节点+根节点->动物2)%2;
至于根节点->动物2的关系我们可以很容易的推导出 根节点->动物2=3 - 动物2-> 根节点
(0 1 2 这几个权值的设定的优越性再次体现了出来)由此,我们便很容易的就可以得到2只动物之间的关系。
接下来我们再处理一下根节点不相同的情况,由于根节点不相同时数据总是正确的,所以我们无需判断真假,只需要将一只动物的根节点连接到另一只动物的根节点上,然后改变子集合根节点的关系即可。
改变结点是个很简单的事,直接find ,然后将其中一个的parent改为另一只动物即可。
但是关系的改变可能第一次时并没有那么好想;
首先我们需要注意的一点是我们的集合都是经过路径压缩的,所以每个集合都只有2层。
还是利用推导出来的第一个公式;
假设数据中的2只动物分别为x,y;
我们可以将关系用公式转化为:根节点2->根结点1=(根节点2->y + y->x + x->根节点1)%3;
综合上面可易知:根节点2->y=3 - y->根节点;
至于 y-x就是数据中的给出的2只动物之间的关系;
最后一个连推导都不用了直接调用即可,这样我们就解决了根节点合并的问题。
这样我们其实就已经解决了这个问题,希望这篇博客可以给大家带来些许的帮助,当然还欢迎各位大神提出宝贵意见,orz