030:发现它,抓住它
查看 提交 统计 提问
描述
一个城市中有两个犯罪团伙A和B,你需要帮助警察判断任意两起案件是否是同一个犯罪团伙所为,警察所获得的信息是有限的。假设现在有N起案件(N<=100000),编号为1到N,每起案件由团伙A或团伙B所为。你将按时间顺序获得M条信息(M<=100000),这些信息分为两类:
-
D [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息表明它们属于不同的团伙所为 -
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;
}
}
}
}