HDU - 6109 数据分割 并查集启发式合并
题意
一个程序会接受如(x_i = x_j)或者(x_i eq x_j)的条件表示式。
现给出(L)行,碰到某一行发现矛盾则输出这一串表达式的个数,开启新的表达式。
e = 1 表示相等,0表示不相等
[i,j,L leq 100000\
x_i , x_j leq L
]
分析
联系之前洛谷做过的一道题,很容易想到这题用并查集来做。
我们可以把相等的点合并到一个并查集。
若遇到不等于,如果两点在同一个并查集,那么输出错误。如果不在同一个并查集,为了标记,在两个并查集的根之间连一条边,表示一种关系。
若遇到相等,如果两个点不在一个并查集,且已经有连边,则输出错误。
否则我们把两个并查集合并,注意这时候就要把边数较小的集合合并到另一个集合,这就是所谓的启发式合并。
这样每个点合并一次规模至少扩大两倍,复杂度就会是(NlogN)的。
不妨用(set)来支持快速删边加边操作。
代码
int pre[maxn];
set<int> e[maxn];
vector<int> ans;
int Find(int x) {
return pre[x] != x ? pre[x] = Find(pre[x]) : x;
}
bool Union(int p, int q) {
if (p != q) {
if (e[p].size() > e[q].size())
swap(p, q);
for (auto it : e[p]) {
if (it == q) return false;
e[it].erase(p);
e[it].insert(q);
e[q].insert(it);
}
}
pre[p] = q;
return true;
}
int main() {
int L = readint();
int x, y, ee;
int Max = -1;
int siz = 0;
for (int i = 1; i <= 100000; i++)
pre[i] = i;
for (int i = 1; i <= L; i++) {
x = readint();
y = readint();
ee = readint();
Max = max(Max, x);
Max = max(Max, y);
int p = Find(x);
int q = Find(y);
if (!ee) {
if (p == q) {
ans.push_back(i - siz);
siz = i;
for (int i = 1; i <= Max; i++)
pre[i] = i, e[i].clear();
Max = -1;
continue;
}
e[p].insert(q);
e[q].insert(p);
}
else if (!Union(p, q)) {
ans.push_back(i - siz);
siz = i;
for (int i = 1; i <= Max; i++)
pre[i] = i, e[i].clear();
Max = -1;
continue;
}
}
cout << ans.size() << '
';
for (int i = 0; i < ans.size(); i++)
cout << ans[i] << '
';
}