NKOJ 2107 【并查集】可爱的猴子
思路:普通并查集+图的遍历更新答案
实现方法
- 首先使用时光倒流思想解决删边的问题。
- 注意提前把没有删过的边提前建上。
- 接着用一个图记录猴子之间的拉手关系,每次要更新答案时都遍历与当前节点连着的节点将其答案更新,只有在 \(1\) 号节点与当前节点断开时才更新答案。
代码
#include<cstdio>
using namespace std;
int n,m,cnt;
int mks[400005][5],ltg[400005][5],last[400005];
int del[400005][5],ans[400005],vis[400005];
struct node{int pre,to;}adj[400005];
void Add(int x,int y){adj[++cnt].to=y,adj[cnt].pre=last[x],last[x]=cnt;}
int f[400005];
int getf(int x){if(x==f[x]) return f[x];return f[x]=getf(f[x]);
}
void dfs(int x,int now){ans[x]=now;vis[x]=1;for(int i=last[x];i;i=adj[i].pre){int lzh=adj[i].to;if(vis[lzh]==0){dfs(lzh,now);}}vis[x]=0;
}
void merge(int x,int y,int now){int fx=getf(x),fy=getf(y);if(fx==fy) return;if(now==-1){f[fx]=fy;return;}if(fx==getf(1)) dfs(fy,now);else if(fy==getf(1)) dfs(fx,now);f[fx]=fy,Add(x,y),Add(y,x);
}
/*错误示范
void merge(int x,int y,int now){int fx=getf(x),fy=getf(y);if(fx==fy) return;if(fx==getf(1)&&now!=-1) dfs(fy,now);else if(fy==getf(1)&&now!=-1) dfs(fx,now);f[fx]=fy,Add(x,y),Add(y,x);//加第二次
}
*/
int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) f[i]=i;for(int i=1;i<=n;i++) scanf("%d%d",&mks[i][1],&mks[i][2]);for(int i=1;i<=m;i++) scanf("%d%d",<g[i][1],<g[i][2]),del[ltg[i][1]][ltg[i][2]]=1;for(int i=1;i<=n;i++){if(mks[i][1]!=-1&&!del[i][1]) merge(i,mks[i][1],-1),Add(i,mks[i][1]),Add(mks[i][1],i);//加一次if(mks[i][2]!=-1&&!del[i][2]) merge(i,mks[i][2],-1),Add(i,mks[i][2]),Add(mks[i][2],i);}for(int i=m;i>=1;i--){int x=ltg[i][1],y=mks[ltg[i][1]][ltg[i][2]];merge(x,y,i);}for(int i=1;i<=n;i++){printf("%d\n",ans[i]-1);}return 0;
}
注意事项
- 一定注意在
merge
的时候注意不要重复加边,会T。