大意:
输入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
- #include
- #include
- #include
- #include
- #include
- 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<
- for (int i = 0; i < L; ++i) {
- scanf("%d %d %d",&a[i],&b[i],&e[i]);
- }
- init();
- solve();
- int sz=ans.size();
- printf("%d\n",sz);
- for (int i = 0; i < sz; ++i) {
- printf("%d\n",ans[i]);
- }
- return 0;
- }