- int find(int x)//并查集找父亲
- {
- if(x!=fa[x]) fa[x]=find(fa[x]);
- return fa[x];
- }
- void add(int x,int y)//合并
- {
- int fx=find(x);
- int fy=find(y);
- if(x!=y) fa[fx]=fy;
- }
P1197 [JSOI2008] 星球大战 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
洛谷p1197星球大战 :并查集+逆向思考_phrame_的博客-CSDN博客
这题由于并查集不好进行删除点的操作,所以反向操作,把删除点反向成一个个添加新的点,注意判断连通块的方法是看,fa[i]==i,那么是一个连通块,父亲相等为一个连通块
- #include<bits/stdc++.h>
- #define int long long
- using namespace std;
- const int N=1e6+100;
- vector<int>ans;
- vector<int> a[N];//存图
- int fa[N];
- int h[N];//存要摧毁的
- int vis[N];//标记是否要摧毁
- int find(int x)//并查集
- {
- if(x!=fa[x]) fa[x]=find(fa[x]);
- return fa[x];
- }
- void add(int x,int y)//合并
- {
- int fx=find(x);
- int fy=find(y);
- if(x!=y) fa[fx]=fy;
- }
- signed main()
- {
- int n,m;
- cin>>n>>m;
- for(int i=0;i<n;i++)
- {
- fa[i]=i;//初始化
- }
- for(int i=1;i<=m;i++)
- {
- int u,v;
- cin>>u>>v;
- a[u].push_back(v);
- a[v].push_back(u);//双向存图
- }
- int k;
- cin>>k;
- for(int i=1;i<=k;i++)
- {
- cin>>h[i];
- vis[h[i]]=1;//标记要摧毁的
- }
- for(int i=0;i<n;i++)
- {
- for(auto x:a[i])//遍历a[i]所有的点
- {
- if(vis[x] ||vis[i])//去掉要摧毁的
- {
- continue;
- }
- add(x,i);//对不摧毁的点合并处理
- }
- }
- int cnt=0;
- for(int i=0;i<n;i++)
- {
- if(!vis[i]&&fa[i]==i)
- {
- cnt++;//没有修复前连通块的个数
- }
- }
- ans.push_back(cnt);//最后一种全摧毁完的答案
- //开始修复,倒推
- for(int i=k;i>=1;i--)
- {
- int x=h[i];
- cnt++;//把它当成独立的连通块
- vis[x]=0;//被修复
- for(auto u:a[x])
- {
- if(!vis[u]&&find(x)!=find(u))
- {
- add(u,x);
- cnt--;//减去不是连通块的
- }
- }
- ans.push_back(cnt);
- }
- reverse(ans.begin(),ans.end());
- for(auto c:ans)
- {
- cout<<c<<endl;
- }
- return 0;
- }
P1525 [NOIP2010 提高组] 关押罪犯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
带权并查集,按权值先排序,然后再进行查找,合并等操作,记住合并操作要在根节点上运作
- #include<bits/stdc++.h>
- using namespace std;
- const int N=1e5+10;
- int n,m,f[40005],x,y;
- struct data{
- int a,b,c;
- }e[N];
- bool cmp(data p,data q)
- {
- return p.c>q.c;//从大到小排
- }
- int find(int x)//并查集,find(x)就是x的根
- {
- if(f[x]!=x) f[x]=find(f[x]);
- return f[x];
- }
- int main()
- {
- cin>>n>>m;
- for(int i=1;i<=m;i++)
- {
- cin>>e[i].a>>e[i].b>>e[i].c;
- }
- for(int i=1;i<=n*2;i++)
- {
- f[i]=i;//先预处理每个人指向自己
- }
- sort(e+1,e+1+m,cmp);
- for(int i=1;i<=2*n;i++)
- {
- //并查集的合并操作都是对根操作
- int x=find(e[i].a);
- int y=find(e[i].b);
- if(x==y)//根相同,代表在同一个集合
- {
- cout<<e[i].c;
- return 0;
- }
- //合并,a到b的敌人b'集合,b到a的敌人a'集合
- f[x]=find(e[i].b+n);
- f[y]=find(e[i].a+n);
- }
- cout<<0;
- return 0;
- }