在比赛的时候直接想到的就是用并查集加Set的做法,但是由于评测爆炸,只交了一发还没有过。赛后有好好想了之前的思路,还参考了一些博客,发现这种做法的确可以AC。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6109
分析:
如果两个元素是相等的那就将它们合并到一起,同时用Set记录不相等的数,开一个Set数组,Set[i]表示与i不相等的数。因此相连的都是相等的,集合中的都是不等的。
如果e = 1, 那就找a, b的根节点fa, fb,如果在Set[fa]中找到fb 或者 在Set[fb]中找到fa, 那就说明存在矛盾,就从这里截断。如果没有矛盾,那就判断两个是不是同根,如果不同根就合并两个集合,并且更新相关的Set[i]
如果e = 0, 先把fa加到Set[fb]中, fb加到Set[fa]中,然后判断两个是不是同根,如果同根就有矛盾。
就这样就可以完成数据分割。
同时,还看到了一个初始化的技巧,就是将所有出现的数字,用Set记录,当出现矛盾,要开始下一组时只需要将Set中的每一个元素对应的Set和数组置位就可以了。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
const int MAXN = 100005;
int fa[MAXN];
set<int> Set[MAXN], con;
int re[MAXN];
int find(int x)
{
if(fa[x] == -1) return x;
return fa[x] = find(fa[x]);
}
void unit(int x, int y)
{
x = find(x);
y = find(y);
if(x != y) fa[x] = y;
}
int main()
{
int L;
re[0] = 0;
int cnt = 0;
scanf("%d", &L);
int i, j, e;
memset(fa, -1, sizeof(fa));
while(L--)
{
scanf("%d%d%d", &i, &j, &e);
con.insert(i), con.insert(j);
int ri = find(i), rj = find(j);
cnt += 1;
if(e == 1)
{
if(Set[ri].count(rj) || Set[rj].count(ri))
{
re[++re[0]] = cnt;
cnt = 0;
for(auto e : con)
{
Set[e].clear();
fa[e] = -1;
}
con.clear();
}
else if(ri != rj)
{
fa[ri] = rj;
for(auto e : Set[ri])
{
Set[rj].insert(e);
Set[e].erase(ri);
Set[e].insert(rj);
}
Set[ri].clear();
}
}
else
{
Set[ri].insert(rj), Set[rj].insert(ri);
if(ri == rj)
{
re[++re[0]] = cnt;
cnt = 0;
for(auto e : con)
{
Set[e].clear();
fa[e] = -1;
}
con.clear();
}
}
}
for(int i = 0; i <= re[0]; ++i)
printf("%d\n", re[i]);
return 0;
}