并查集撤消操作

 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
", 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
" : "NO
");
    return 0;
}
//https://blog.csdn.net/Shili_Xu/article/details/78726516

  

原文地址:https://www.cnblogs.com/cutemush/p/14665081.html