并查集撤消操作
https://www.cnblogs.com/y2823774827y/p/11600426.html
int parent[maxn],siz[maxn]; //按秩合并用siz,siz小的连向siz大的 vector<pii> tmp; //记录上次合并用到的节点 int find(int p) //递归找根节点,注意没有路径压缩 { if( p == parent[p] ) return p; return find(parent[p]); } void done(pii v) //合并两个点 { int x = find(v.fi),y = find(v.se); if( x == y ) { tmp.push_back(mp(-1,-1)); //已经在同一集合中,不用再连了 continue; } if( siz[x] > siz[y] ) swap(x,y); //一定是小的连向大的 tmp.push_back(mp(x,y)); //把信息存入栈中 parent[x] = y; //合并一下 siz[y] += siz[x]; } void undone() //反悔操作 { pii z = tmp.back(); //取出栈顶元素 tmp.pop_back(); if( z.fi == -1 ) continue; //那时候没有操作,那么也不需要回退 parent[z.fi] = z.fi; //直接回退,做了啥就别做啥 siz[z.se] -= siz[z.fi]; }
#include<bits/stdc++.h> #define LL long long using namespace std; typedef pair<int, int> PII; struct node{ int x, y, z; }e[500010]; struct query{ int x, y, id; friend bool operator < (const query &a, const query &b){ return a.id<b.id; } }; int father[500010], ret[500010], n, m, qu; stack<PII > s; vector<query> v[500010]; vector<PII > g[500010]; int fd(int x){ if(father[x]<0){ return x; } else{ return fd(father[x]); } } void work(int x, int y) { int fx=fd(x), fy=fd(y); if(fx==fy){ return ; } if(father[fx]<=father[fy]) { father[fx]+=father[fy]; father[fy]=fx; } else { father[fy]+=father[fx]; father[fx]=y; } } void solve(int cnt, int x, int y) { while(!s.empty()){ s.pop(); } for (int i=x; i<=y; ++i) { int fx=fd(v[cnt][i].x), fy=fd(v[cnt][i].y); if(fx==fy){//如果有一条边添加不进去就不可以 ret[v[cnt][i].id]=1; } else if(father[fx]<=father[fy]){ s.push(make_pair(fy, father[fy])); father[fy]=fx; } else{ s.push(make_pair(fx, father[fx])); father[fx]=fy; } } while(!s.empty()) { father[s.top().first] = s.top().second; s.pop(); } } int main(){ memset(father, -1, sizeof(father)); scanf("%d%d", &n, &m); for(int i=1; i<=m; i++){ scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z); g[e[i].z].push_back(make_pair(e[i].x, e[i].y)); //按权值 保存原始边 } scanf("%d", &qu); for(int i=1; i<=qu; i++) { int opnum; scanf("%d", &opnum); for(int j=1; j<=opnum; j++) { int x; scanf("%d", &x); v[e[x].z].push_back({e[x].x, e[x].y, i}); //按权值 保存所有的查询的边 } } for(int i=1; i<=500000; i++) { sort(v[i].begin(), v[i].end()); } for(int i=1; i<=500000; i++) { for(int j=0; j<g[i-1].size(); j++) {//把权值<i的全部添加进去。形成的每个连通块包含的点集是一样的 work(g[i-1][j].first, g[i-1][j].second); } int sz=v[i].size(); if(sz==0) { continue; } int now=0; while(now<sz) { int j=now; while(j+1<sz&&v[i][j+1].id==v[i][j].id){ //找到把属于同一个询问的所有的边区间 j++; } solve(i, now, j);//判断这个区间 now=j+1; } } for(int i=1; i<=qu; i++) { printf("%s\n", ret[i]?"NO":"YES"); } return 0; } #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int MAXN = 5e5 + 5; struct edge { int from, to, dist; }; int n, m, q, cnt; int fa_1[MAXN], fa_2[MAXN], kase[MAXN]; bool wa[MAXN]; edge e[MAXN]; vector<int> gra[MAXN]; vector<pair<int, int> > qr[MAXN]; int findfather_1(int x) { return (fa_1[x] == x ? x : fa_1[x] = findfather_1(fa_1[x])); } int findfather_2(int x) { if (kase[x] != cnt) { kase[x] = cnt; fa_2[x] = findfather_1(x); } return (x == fa_2[x] ? x : fa_2[x] = findfather_2(fa_2[x])); } int main() { scanf("%d %d", &n, &m); int a, b, c, mx = 0; for (int i = 1; i <= m; i++) { scanf("%d %d %d", &a, &b, &c); e[i].from = a; e[i].to = b; e[i].dist = c; gra[c].push_back(i); mx = max(mx, c); } int k, x; scanf("%d", &q); for (int i = 1; i <= q; i++) { scanf("%d", &k); for (int j = 1; j <= k; j++) { scanf("%d", &x); qr[e[x].dist].push_back(make_pair(i, x)); } } memset(wa, false, sizeof(wa)); memset(kase, 0, sizeof(kase)); for (int i = 1; i <= n; i++) fa_1[i] = i; cnt = 0; for (int i = 1; i <= mx; i++) { sort(qr[i].begin(), qr[i].end()); for (int j = 0; j < qr[i].size(); j++) { if (j == 0 || qr[i][j].first != qr[i][j - 1].first) cnt++; int x = findfather_2(e[qr[i][j].second].from); int y = findfather_2(e[qr[i][j].second].to); if (x == y) wa[qr[i][j].first] = true; fa_2[y] = x; } for (int j = 0; j < gra[i].size(); j++) { int x = findfather_1(e[gra[i][j]].from); int y = findfather_1(e[gra[i][j]].to); fa_1[y] = x; } } for (int i = 1; i <= q; i++) printf(!wa[i] ? "YES\n" : "NO\n"); return 0; } //https://blog.csdn.net/Shili_Xu/article/details/78726516