文章目录
并查集
//此处优化为更新x的祖先时顺便更新了x父亲的祖先也为x的祖先
int ff(x){return fa[x]==x?x:fa[x]=ff(fa[x]);}
int join(int x,int y){x=ff(x),y=ff(y);x=fa[y];}
P2661 [NOIP2015 提高组] 信息传递
用并查集难的地方是在寻找父亲和合并过程的变化。要了解合并和寻找过程对题目变量的影响。
一旦
i
i
i查询的
x
x
x和
i
i
i不是直接父子关系,继续向上查找合并就应该算成另外一轮,并且是当前轮之前的轮数确定的并集关系。
同时模板中查找父亲时顺便更新父亲的祖先的优化也不能加入,因为这个优化的作用在于将
x
x
x并查集内的所有节点都变成
x
x
x祖先的直接后继,将整个并集变成二层结构,从而减少
x
x
x的父亲查找其祖先的次数,但是这个次数恰恰是我们需要的轮数。
#include<bits/stdc++.h>
using namespace std;
const int N=200007;
int fa[N],n,ans=0x3f3f3f3f,cnt;
int ff(int x){
cnt++;
return fa[x]==x?x:ff(fa[x]);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=n;i++){
cnt=0;
int x;cin>>x;
if(ff(x)!=i){
fa[i]=x;
}
else ans=min(cnt,ans);
}
cout<<ans<<endl;
return 0;
}
P1525 [NOIP2010 提高组] 关押罪犯
敌人的敌人是朋友,只要保存当前边的两个人原先有的敌人,再交叉将对方与自己的敌人合在一个并查集即可。这个题很像食物链,就是在扩展空间保存不同关系。
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+7,M=1e5+7;
int n,m;
struct Peo{
int fa,enemy;
}peo[N];
struct Node{
int u,v,w;
bool operator<(const Node &a){return w>a.w;}
}node[M];
int ff(int x){return peo[x].fa==x?x:peo[x].fa=ff(peo[x].fa);}
int join(int x,int y){x=ff(x),y=ff(y);peo[x].fa=y;}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)peo[i].fa=i;
for(int i=1;i<=m;i++)cin>>node[i].u>>node[i].v>>node[i].w;
sort(node+1,node+1+m);
for(int i=1;i<=m;i++){
int u=node[i].u,v=node[i].v;
if(ff(u)==ff(v)){
cout<<node[i].w<<endl;return 0;
}
if(!peo[u].enemy)peo[u].enemy=v;
else join(v,peo[u].enemy);
if(!peo[v].enemy)peo[v].enemy=u;
else join(u,peo[v].enemy);
}
cout<<0<<endl;
return 0;
}
P1197 [JSOI2008]星球大战
先将所有的连边存储起来,按顺序保存每个要炸毁的点,再连接那些不被炸毁的边,此时的连通块个数就是炸毁
k
k
k个点之后的连通块个数,相当于是最后的连通块个数,因此用栈来存储答案。接下来逆序遍历
k
k
k个要炸毁的点,每次添加一个点及其连边。每次连边都是将两个连通块变成一个,因此连通块总个数减少1。
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+7,M=2e5+7;
vector<int>son[N];
int fa[N],n,m,k,broken[N],stk[N],top;
bool vis[N];
int ff(int x){return fa[x]==x?x:fa[x]=ff(fa[x]);}
int main(){
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;
son[u].push_back(v);son[v].push_back(u);
}
cin>>k;
int cnt=n-k;
for(int i=1;i<=k;i++){
cin>>broken[i];
vis[broken[i]]=true;
}
for(int u=0;u<n;u++){
if(vis[u])continue;
int fu=ff(u);
for(int j=0;j<son[u].size();j++){
int v=son[u][j];
if(vis[v])continue;
v=ff(v);
if(v!=fu){
fa[v]=fu;
cnt--;
}
}
}
stk[++top]=cnt;
for(int i=k;i;i--){
int u=broken[i];
vis[u]=false;
int fu=ff(u);
cnt++;
for(int j=0;j<son[u].size();j++){
int v=son[u][j];
if(vis[v])continue;
v=ff(v);
if(v!=fu){
fa[v]=fu;
cnt--;
}
}
stk[++top]=cnt;
}
while(top){
cout<<stk[top--]<<endl;
}
return 0;
}