为什么这样写并查集会错
查看原帖
为什么这样写并查集会错
1229671
Greenqwq楼主2024/6/9 14:09

如下图所示:并查集 find 的注释部分,为什么是错的?

//可撤销并查集
//好TM难找错误
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<vector>
#include<stack>//可撤销并查集

#define int long long
// #define x first
// #define y second

using namespace std;

typedef pair<int,int>PII;

const int N = 1e6+10;

int a[N],p[N],siz[N],siz1[N];
int p1[N];
int n,m;
int col,tmp;
unordered_map<int,int>S;//颜色对的映射,类似哈希表
struct E{
    int x,y;
}e[N];
vector<E>ve[N];//存储下是 i 颜色对的边有哪些
int insans[N],sta[N];//insans表示颜色中可以互达的点对个数
//sta表示跨颜色之间的贡献
stack<int> stk1;//stk1表示合并的根节点
stack<PII> stk2;//stk2表示已合并到其他根的根大小
int colp,tp;//colp表示图上有多少颜色对在图上出现

int find(int x){
    // if(x!=p[x])p[x]=find(p[x]);
    // return p[x];
    if(p[x]==x)return x;
	return find(p[x]);
}

int find1(int x){
    // if(x!=p1[x])p1[x]=find1(p1[x]);
    // return p1[x];
    if(p1[x]==x)return x;
	p1[x]=find1(p1[x]);return p1[x];
}

void merge(int x,int y){
	int xx=find(x),yy=find(y);
	
	if(xx==yy)return;
	
	if(siz[xx]>siz[yy])swap(xx,yy);
	
	stk1.push(xx);
	stk2.push(make_pair(yy,siz[yy]));
	siz[yy]+=siz[xx];
	p[xx]=yy;
}

void undo(){
    //然后撤销时,直接拿出栈顶的信息,进行复原,再弹栈即可。
    p[stk1.top()]=stk1.top();
    siz[stk2.top().first]=stk2.top().second;
    stk1.pop();
    stk2.pop();
}

signed main(){
    scanf("%lld%lld",&n,&m);
    
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        p1[i]=i;
        col=max(col,a[i]);
    }
    
    for(int i=2;i<=n;i++){
        int x;
        scanf("%lld",&x);
        if(a[x]==a[i]){
            p1[find1(x)]=find1(i);
        }else{
            if(a[x]<a[i]){
                /*如果直接对一个 pair 用 map 进行映射会 TLE,然后 pair 又是不能直接用
                unordered_map 映射的。所以说,我们先把这个 pair (x,y) 给映射成一个整数 
                x*10^6+y,再用 unordered_map 进行映射。
                */
                tmp=a[x]*1e6+a[i];
            }else{
                tmp=a[i]*1e6+a[x];
            }
            
            if(!S.count(tmp)){//如果找不到此颜色,则开一个
                S[tmp]=++colp;
            }
            tp=S[tmp];
            ve[tp].push_back({x,i});
        }
    }
    
    
    for(int i=1;i<=n;i++){
        p1[i]=find1(i);
        siz1[p1[i]]++;
    }
    
    
    for(int i=1;i<=n;i++){
        if(p1[i]==i){
            insans[a[i]]+=siz1[i]*siz1[i];
        }
    }
    
    for(int i=1;i<=n;i++)p[i]=p1[i];
    
    for(int i=1;i<=n;i++)siz[i]=siz1[p1[i]];
    //前面的过程为预处理各个颜色缩点连线处理
    
    for(int i=1;i<=colp;i++){
        for(int j=0;j<ve[i].size();j++){
            int xx=find(ve[i][j].x),yy=find(ve[i][j].y);
            sta[i]+=siz[xx]*siz[yy]*2;//连边
            
            // int a=find(ve[i][j].x),b=find(ve[i][j].y);
            // if(a!=b){
            //     if(siz[a]>siz[b])swap(a,b);
            //     stk1.push(a);//先记录下较小集合的父亲
            //     stk2.push({b,siz[b]});//再记录下较大集合的父亲和大小
            //     siz[b]+=siz[a];
            //     p[a]=b;
                
            // }
            merge(ve[i][j].x,ve[i][j].y);
        }
        for(int j=0;j<ve[i].size();j++)undo();
    }
    
    
    while(m--){
        int x,y;
        scanf("%lld%lld",&x,&y);
        int ans=insans[x]+insans[y];
        if(x>y)swap(x,y);
        tmp=x*1e6+y;
        if(!S.count(tmp)){
            printf("%lld\n",ans);
        }else{
            //没有边链接
            
            tp=S[tmp];
            ans+=sta[tp];
            printf("%lld\n",ans);
        }
    }
    return 0;
}
2024/6/9 14:09
加载中...