已经不想复制链接了…
看到题目后,没学过并查集的话会想到用3个数组,每个询问都查询或添加,可看一看这惊人的数据,emmm还是算了吧。(我当时就是这么想的 )
学了并查集后,也发现这不是普通的并查集,虽然有判断是否一个种类,可无法判断是否互相吃。这时候就该引出你了,虚点!
将一个点扩大成3个点(按题目而定,如果点较多,虚点也无能为力),将A,B,C的关系转换成这样:
设A被A+n吃,A+n被A+n2吃,A+n2被A吃,B与C以此类推,则可将
A,B+n,C+n2为一类
A+n,B+n2,C为一类
A+n*2,B,C+n为一类
然后,就好了吧。
#include<cstdio>
const int N = 50000;
int n, T, ba[N * 3 + 5], f, u, v, ans;
void makeSet() {
for(int i = 1; i <= n * 3; i ++)
ba[i] = i;
}
int findSet(const int x) {
return x == ba[x] ? x : ba[x] = findSet(ba[x]);
}
void unionSet(const int x, const int y) {
ba[findSet(x)] = findSet(y);
}
int main() {
scanf("%d %d", &n, &T);
makeSet();
while(T --) {
scanf("%d %d %d", &f, &u, &v);
if(u > n || v > n || (u == v && f == 2)) {
ans ++;
continue;
}
if(f == 1) {
if(findSet(u) == findSet(v + n) || findSet(u) == findSet(v + (n << 1)))
ans ++;
else {
unionSet(u, v);
unionSet(u + n, v + n);
unionSet(u + (n << 1), v + (n << 1));
}
}
else {
if(findSet(u) == findSet(v) || findSet(u) == findSet(v + (n << 1)))
ans ++;
else {
unionSet(u, v + n);
unionSet(u + n, v + (n << 1));
unionSet(u + (n << 1), v);
}
}
}
printf("%d\n", ans);
return 0;
}
注:判断时判断是否一类而非不同类,因为先开始大家都是不一样的。
如果有不对的地方,请大佬指出哦。