题意:
与吃瓜题1题意一致,只是西瓜的种类从两种变成了无数种。
题解:
在吃瓜题1里,由于只有两类西瓜,因此我们可以使用分类的方法来表示异同。而在这题中,由于种类数无穷,我们只有记录每个节点的对立节点,在询问x与y的关系时,我们查询x的对立节点集合中是否出现y节点。
那么如何记录对立节点集呢?我们采用set
当两个节点确定异种的时候,我们分别在set[x]和set[y]中添加y和x
当两个节点确定同种的时候,我们将他们的set合并到一起。合并是要注重技巧的!每次,我们选择较小的set,添加到较大的set中。因此,最坏情况其实就类似于一个二叉树的合并次数,大约为N次。
#include<cstdio>
#include<set>
using namespace std;
const int maxn = 1e6 + 5;
int pre[maxn];
set<int>diff[maxn];
int findset(int x) {
if (x == pre[x]) return x;
else return pre[x] = findset(pre[x]);
}
void unionset(int x,int y) {
int fx = findset(x);
int fy = findset(y);
if (diff[fx].size() < diff[fy].size()) {
//swap(fx, fy);
pre[fx] = fy;
for (set<int>::iterator it = diff[fx].begin(); it != diff[fx].end(); it++) {
int tmp = *it;
tmp = findset(tmp);
diff[fy].insert(tmp);
diff[tmp].insert(fy);
}
}
else {
//swap(fx, fy);
pre[fy] = fx;
for (set<int>::iterator it = diff[fy].begin(); it != diff[fy].end(); it++) {
int tmp = *it;
tmp = findset(tmp);
diff[fx].insert(tmp);
diff[tmp].insert(fx);
}
}
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i <= n; i++) {
pre[i] = i;
}
while(m--) {
char s[3];
int x, y, id;
scanf("%s", s);
if (s[0] == 'A') {
scanf("%d%d%d", &x, &y, &id);
if (id == 1) {
unionset(x, y);
}
else {
int fx = findset(x);
int fy = findset(y);
diff[fx].insert(fy);
diff[fy].insert(fx);
}
}
else {
scanf("%d%d", &x, &y);
int fx = findset(x);
int fy = findset(y);
if (fx==fy) printf("1
");
else if (diff[fx].find(fy) != diff[fx].end())
printf("2
");
else printf("3
");
}
}
return 0;
}