博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HDU 1829 A Bug's Life
阅读量:5366 次
发布时间:2019-06-15

本文共 3712 字,大约阅读时间需要 12 分钟。

这道题网上主要有两种方法,我都想了一遍,写点总结,我自己看。

第一种是找到的一种好理解的方法,sex[a]=b相当于点a的原配是b,之后若出现a c,就把csex[a]=b合并,相当于情敌之间合并到一起,因为是情敌了,所以肯定是同性了,之后若有点对在同一集合那么就错误了。

这种思想是完全等价于题目要求,同性对的判定,只有出现在情敌之间才能被判定出来,情敌包括直接情敌(三角)和间接情敌(奇环,奇数个边的环),举一个间接情敌的例子,ad是直接情敌、共同喜欢bae是直接情敌、共同喜欢c,那么de也是情敌,这俩要是连上了那么就成了一个奇环(五边形)。
所以问题就转化成了怎么判断二者是不是情敌,那么这种方法的思想就是根据已知信息不断合并已知情敌,当新出现的点对属于同一集合那么就gg了,每个集内的任意两点必然是情敌(间接或直接),不属于同一集的两点在当前信息下必然不是情敌,注意,判断两点属于同一个并查集是符合传递性的。

#include 
#include
#include
#include
using namespace std;const int MAXN = 2005;int T, N, M;int pre[MAXN];int sex[MAXN];bool flag;void init(){ memset(pre, -1, sizeof pre); memset(sex, 0, sizeof sex); flag = 0;}int find(int x){ if (pre[x] < 0) return x; return pre[x] = find(pre[x]);}void u(int a, int b){ int f1 = find(a); int f2 = find(b); if (f1 == f2) return; if (pre[f1] <= pre[f2]) { pre[f1] += pre[f2]; pre[f2] = f1; } else { pre[f2] += pre[f1]; pre[f1] = f2; }}int main(){ int a, b; scanf("%d", &T); for (int i = 1; i <= T; i++) { init(); scanf("%d%d", &N, &M); for (; M--;) { scanf("%d%d", &a, &b); if (flag) continue; if (find(a) == find(b)) { flag = 1; continue; } if (!sex[a]) sex[a] = b; else u(sex[a], b); if (!sex[b]) sex[b] = a; else u(sex[b], a); } printf("Scenario #%d:\n", i); if (flag) printf("Suspicious bugs found!\n\n"); else printf("No suspicious bugs found!\n\n"); } return 0;}

再写一种,这种方法的思想比较难写出来。我的理解,sign数组的意义是不确定的,有时指与父节点的一致情况,有时指与根节点的一致情况,当然,相对来说,其实都是指与父节点的一致情况。要搞清这个,关键在于理解find函数的递归过程,首先符合压缩规则这没问题,

sign[x] = (sign[x] + sign[pre[x]]) & 1;

这一句完成了sign意义的转化,sign[x]旧值指与父节点的一致情况(上一次被赋值时是直接指向根节点,然而当时的根节点已不是现在的根节点),sign[x]新值是指与根节点的一致情况,pre[x]在现在还是指父节点,马上下一句就变成根节点了,因为父节点在上次递归返回中已被直接指向根节点,所以sign[pre[x]]的意义及值也得到更新,所以sign[pre[x]]是指父节点与根节点的一致情况,所以,根据父节点过渡,负负得正,得到当前节点与根节点的一致情况。

所以,find函数先根据访问点层层上升直到根节点,然后从根节点下降,把沿途经过的每个结点直接指向根节点并赋予其正确的与根节点的一致情况。

接下来我说一说union/merge函数,之前我想的是,首先合并肯定不会涉及flag,合并是根1直接指向根2,那么根1及其子树可能都要变sign,然而,只用更新根1的sign就行了,因为每次查询根的时候都会把链粉碎并自上而下地赋予sign,所以只要保证子树的根 根1sign正确就行了。那么,原先的两个查询点n1 n2已经直接指向根1根2,且n1 n2一定不一致,根1到根2的一致情况也就可以通过下式确定了。

sign[f1] = (sign[n1] + sign[n2] + 1) & 1;

#include 
#include
#include
#include
using namespace std;const int MAXN = 2005;int T, N, M;int pre[MAXN];int sign[MAXN]; // 0 表示一致,1 表示不一致bool flag;void init(){ memset(pre, -1, sizeof pre); memset(sign, 0, sizeof sign); flag = 0;}int f(int x){ if (pre[x] < 0) return x; int t = f(pre[x]); sign[x] = (sign[x] + sign[pre[x]]) & 1; return pre[x] = t;}void u(int n1, int n2){ int f1 = f(n1); int f2 = f(n2); if (f1 == f2) { if (sign[n1] == sign[n2]) flag = 1; return; } if (pre[f1] <= pre[f2]) { pre[f1] += pre[f2]; pre[f2] = f1; sign[f2] = (sign[n1] + sign[n2] + 1) & 1; } else { pre[f2] += pre[f1]; pre[f1] = f2; sign[f1] = (sign[n1] + sign[n2] + 1) & 1; }}int main(){ int a, b; scanf("%d", &T); for (int i = 1; i <= T; i++) { init(); scanf("%d%d", &N, &M); for (; M--;) { scanf("%d%d", &a, &b); if (flag) continue; u(a, b); } printf("Scenario #%d:\n", i); if (flag) printf("Suspicious bugs found!\n\n"); else printf("No suspicious bugs found!\n\n"); } return 0;}

转载于:https://www.cnblogs.com/CrossingOver/p/10704883.html

你可能感兴趣的文章
HAL层三类函数及其作用
查看>>
web@h,c小总结
查看>>
Data Structure 基本概念
查看>>
NEYC 2017 游记
查看>>
[搬运] 写给 C# 开发人员的函数式编程
查看>>
core--线程池
查看>>
他山之石:加载图片的一个小问题
查看>>
shell - 常识
查看>>
Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)
查看>>
PKUWC2018 5/6
查看>>
As-If-Serial 理解
查看>>
洛谷P1005 矩阵取数游戏
查看>>
在Silverlight中使用HierarchicalDataTemplate为TreeView实现递归树状结构
查看>>
无线通信基础(一):无线网络演进
查看>>
关于python中带下划线的变量和函数 的意义
查看>>
linux清空日志文件内容 (转)
查看>>
Ajax : load()
查看>>
MySQL-EXPLAIN执行计划Extra解释
查看>>
图片点击轮播(三)-----2017-04-05
查看>>
直播技术细节3
查看>>