大意:
输入L组a,b,e,e=1表示a和b在一个集合里面,0表示不在一个集合里面。
让我们做数据的分组,而成为一组数据的条件是:前i-1组数据都是互不矛盾的,而第i组数据与前i-1组数据的意思有了矛盾。
输出就是一共能把数据划分为几组,每组有多少个输入数据。
总体思路是并查集加上一个set维护不相等的集合,set[x]中的所有元素代表和x不相等的点y的集合。
假设现在处理(a,b,e),思路如下:
1.首先求得a和b在并查集中的根节点x和y。
2.如果e等于1,表示a和b在一个集合里:
若x和y相等,即a和b在一个集合里,我们直接跳过处理。
若G[x]中包含了y,说明之前出现过(a,b,0)或者(b,a,0),那么我们做一次划分。
若G[x]中无y,那么合并两个并查集集合x和y,除了修改x指向y,我们还要维护set集合,即把原来和x不在一个集合里边的G[i]里边的x修改成y。
3.如果e等于0,表示a和b不在一个集合里:
若x和y相等,说明有矛盾,直接划分一次。
否则直接维护一下G[x]和G[y],分别插入y和x,以表示两个集合是不等的。
代码如下所示:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef long long ll;
const ll N=1e5+10;
int L;
int fa[N],a[N],b[N],e[N];
set<int> G[N];
vector<int> ans;
int find_s(int x){
return x == fa[x] ? x : fa[x] = find_s(fa[x]);
}
void init(){
for (int i = 0; i < L; ++i) {
fa[i]=i;
G[i].clear();
}
}
void solve(){
int cnt=0;
for (int i = 0; i < L; ++i) {
cnt++;
int x = find_s(a[i]);
int y = find_s(b[i]);
if (e[i]){
if (x==y) continue;
else if (G[x].find(y)!=G[x].end()){
ans.push_back(cnt);
cnt=0;
init();
}else{
set<int>::iterator it;
for (it = G[x].begin(); it != G[x].end(); ++it) {
G[y].insert(*it);
G[*it].erase(x);
G[*it].insert(y);
}
G[x].clear();
fa[x]=y;
}
}else{
if (x==y){
ans.push_back(cnt);
cnt=0;
init();
}else{
G[x].insert(y);
G[y].insert(x);
}
}
}
}
int main()
{
scanf("%d",&L);
// cout<<L<<endl;
for (int i = 0; i < L; ++i) {
scanf("%d %d %d",&a[i],&b[i],&e[i]);
// cout<<a[i]<<","<<b[i]<<","<<e[i]<<endl;
}
init();
solve();
int sz=ans.size();
printf("%d\n",sz);
for (int i = 0; i < sz; ++i) {
printf("%d\n",ans[i]);
}
return 0;
}