北京大学ACM-ICPC竞赛训练暑期课 / 练习 030:发现它,抓住它

030:发现它,抓住它


        查看      提交      统计      提问


总时间限制: 1000ms
内存限制: 65536kB

描述

  一个城市中有两个犯罪团伙A和B,你需要帮助警察判断任意两起案件是否是同一个犯罪团伙所为,警察所获得的信息是有限的。假设现在有N起案件(N<=100000),编号为1到N,每起案件由团伙A或团伙B所为。你将按时间顺序获得M条信息(M<=100000),这些信息分为两类:

  1. D [a] [b]
    其中[a]和[b]表示两起案件的编号,这条信息表明它们属于不同的团伙所为

  2. A [a] [b]
    其中[a]和[b]表示两起案件的编号,这条信息需要你回答[a]和[b]是否是同一个团伙所为
    注意你获得信息的时间是有先后顺序的,在回答的时候只能根据已经接收到的信息做出判断。

输入

  第一行是测试数据的数量T(1<=T<=20)。
  每组测试数据的第一行包括两个数N和M,分别表示案件的数量和信息的数量,其后M行表示按时间顺序收到的M条信息。

输出

  对于每条需要回答的信息,你需要输出一行答案。如果是同一个团伙所为,回答"In the same gang.",如果不是,回答"In different gangs.",如果不确定,回答”Not sure yet."。

样例输入

1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4

样例输出

Not sure yet.
In different gangs.
In the same gang.

        查看      提交      统计      提问


思路:

方法一:

彼此的确定为对立关系的之间,记录彼此的父亲节点

  • 第一种情况:两个点为对立关系。那么就建立两颗树,这两个节点定位父亲节点,一旦有一个点与其中一个有对立关系,套用第二种情况
  • 第二种情况:有一个点与一棵树为对立关系,如果有一个点确定为一棵树的对立点,那么就找到这个树的对立节点的父节点,让此点连通此点所在的树都放入这个树的对立节点的树中。
#include<iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <functional>
#include <ctime>
#include <iomanip>
#include <numeric>
#include <sstream>
#include <algorithm>
#define ll int
#define PI acos(-1)
#define mes(x,y) memset(x,y,sizeof(x))
#define lp pair<ll, ll>
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
ll n, m, i, j, k = 0, t, w, flag, x, y, z, sum;
string s1, s2, s;
ll tree[1000010], relative[1000010];
ll head(ll x) {
	return x == tree[x] ? x : tree[x] = head(tree[x]);
}
int main() {
	while (cin >> k) {
		while (k--) {
			cin >> n >> m;
			for (i = flag = 0, t = w = -1; i <= n; i++)tree[i] = i, relative[i] = 0;
			for (i = 0; i < m; i++) {
				cin >> s >> x >> y;
				x = head(x); y = head(y);
				//考虑x和y可能为树中一点,找到他们的父亲节点
				if (s == "D") {
					ll a = relative[x], b = relative[y];
					if (a) {
						a = head(a);
						if (a != y)tree[a] = y;
					}//如果x有对立树,将y放入x的对立树中
					if (b) {
						b = head(b);
						if (b != x)tree[b] = x;
					}//如果y有对立树,将x放入y的对立树中
					relative[x] = y; relative[y] = x;
					//对立父亲节点彼此指向
				}
				else {
					if (x == y) cout << "In the same gang." << endl;
					else if (relative[x] == y)cout << "In different gangs." << endl;
					else cout << "Not sure yet." << endl;
				}
			}
		}
	}
}

方法二:

看关系的题,根据我匮乏的学到的算法,嗯,是并查集,啊哈,蒙对了!!!!
这道题主要利用了中间变量的关系,比如在最一开始学习C语言的时候,我们将x的值和y的值进行交换,我们回去用到一个中间变量,进行交换。

x=1;y=2;
t=x;
y=x;
x=t;

这一题主要运用的是对父亲节点为中间变量,处理点与点的关系。

  • 这道题会让我们处理,单独点与单独点,单独点和树,树与树,树中点和树中点,单独点和树中点,树中点和树。
  • 那么当我们把单独点也看作为一颗树,就变简单一些,只要我们去处理树与树,树中点和树中点,树中点和树。
  • 那么处理好这两个关系的最好办法就是处理他们最终的父亲节点和父亲节点的关系就好了

为了压缩时间,再去用到次节点的时候,再去把次节点以上的节点依次跟新,直到完成父亲节点和此节的关系。

#include<iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <functional>
#include <ctime>
#include <iomanip>
#include <numeric>
#include <sstream>
#include <algorithm>
#define ll int
#define PI acos(-1)
#define mes(x,y) memset(x,y,sizeof(x))
#define lp pair<ll, ll>
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const ll inf = 10000000000000005;
ll n, m, i, j, k = 0, t, w, flag, x, y, z, sum;
string s1, s2, s;
ll tree[100010], relative[100010];
ll head(ll x) {

	if (tree[tree[x]] == tree[x])return tree[x];
	//原来是父亲节点的点和现在父亲节点不处理 

	head(tree[x]);
	//利用递归,将这条线上的全部点更新一遍 ,以求所有点都只指向一个点
	//首先跟新最前方父亲节点的下一个节点和父亲节点的下下一个节点 
	//接着是下下节点和下下下节点,依次类推,直到到当前节点

	relative[x] = (relative[x] + relative[tree[x]]) & 1;
	//处理节点和父亲的关系,就要看自己的上一节点
	//如果现在节点是1那么上一节点原来必然是0,
	//以上一节点为跳板处理这个点与父亲接点的关系 

	return tree[x] = tree[tree[x]];
	//去掉已经不是父亲节点,指向现在的父亲节点 

}
void cmp(ll x, ll y) {
	ll xx = head(x), yy = head(y);
	//查看两个点的父亲都是谁,顺便将两个点更新到直接连接父亲节点 

	if (xx != yy) {

		tree[xx] = yy;
		//让一个父亲节点指向另一个父亲节点 

		relative[xx] = (relative[x] + relative[y] + 1) & 1;
		//处理两个父亲节点的关系 

	}
}
//这之下的东西不重要主要是head()和cmp()hao函数看懂了,下面可以自己写,也是对并查集的小练习
int main() {
	while (cin >> k) {
		while (k--) {
			cin >> n >> m;
			for (i = 0, t = w = -1; i <= n; i++)tree[i] = i, relative[i] = 0;
			while (m--) {
				cin >> s >> x >> y;
				if (s == "D")cmp(x, y);
				else cout << (head(x) != head(y) ? "Not sure yet." : (relative[x] == relative[y] ? "In the same gang." : "In different gangs.")) << endl;
			}
		}
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ACM-ICPC(国际大学生程序设计竞赛)是一项全球性的大学生程序设计比赛,每年吸引来自世界各地的顶尖大学代表队参与。ACM-ICPC竞赛的核心内容是团队编程和问题解决能力。 首先,ACM-ICPC竞赛对参赛选手的编程能力要求很高。参赛队伍需要在规定的时间内解决一系列的算法问题,这些问题常常包含复杂的数据结构和算法,要求选手在有限的时间内设计和实现高效的程序。 其次,ACM-ICPC竞赛强调团队协作。每个队伍由三名选手组成,他们需要分工合作,保持良好的沟通与协调,共同解决问题。团队成员需要相互理解、相互信任,快速地协商和决策,同时要保持高效的任务分配和时间管理。 此外,ACM-ICPC竞赛也需要选手具备良好的问题解决能力。这些问题往往是实际应用或理论推导相关的,选手需要从数学、计算机科学和算法等多个角度出发,找到最佳解决方案。在面对问题时,选手需要对问题进行分析、抽象和建模,运用各种算法和数据结构进行解决。 对于参赛选手来说,ACM-ICPC提供了一个学习与交流的平台。在比赛中,选手可以接触到不同国家和地区的优秀程序设计人才,学习他们的思维方式和编程技巧。同时,ACM-ICPC还举办了一系列的培训和研讨会,让选手有机会深入了解计算机科学和算法领域最新的研究成果。 之,ACM-ICPC国际大学生程序设计竞赛是一个挑战性与学习性兼具的比赛。它要求选手具备扎实的编程技能、团队合作能力和问题解决能力。参与此竞赛不仅可以锻炼自己的编程能力,还能与全球的顶尖程序设计人才进行交流,拓宽自己的视野和思维方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GUESSERR

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值