如下图所示:并查集 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;
}