题目大意:求联通块的数量
显然可以想到tarjan缩点之后统计出度为0的点的数量
但这题会MLE
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+10;
int n,len=0,tot=0,cnt=0,scc=0,top=0,ans=0,last[N],head[N],
dfn[N],low[N],ins[N],v[N],bl[N],s[N],fa[N];
struct Edge{int to,next;Edge(int to=0,int next=0):to(to),next(next){}}e[N<<1],E[N<<1];
void add_edge(int u,int v){e[++len]=Edge(v,last[u]);last[u]=len;}
void add_Edge(int u,int v){E[++tot]=Edge(v,head[u]);head[u]=tot;}
void tarjan(int x)
{
int now=0;
dfn[x]=low[x]=++cnt;
s[++top]=x;ins[x]=1;
for(int i=last[x];i;i=e[i].next) {
if(!dfn[e[i].to]){
tarjan(e[i].to);
low[x]=min(low[x],low[e[i].to]);
}else if(ins[e[i].to])low[x]=min(low[x],dfn[e[i].to]);
}
if(dfn[x]==low[x]) {
++scc;
while(now!=x) {
now=s[top--];ins[now]=0;
bl[now]=scc;
v[scc]++;
}
}
}
void rebuild()
{
tot=0;
for(int i=1;i<=n;i++)
for(int j=last[i];j;j=e[i].next)
if(bl[i]!=bl[e[i].to])
add_Edge(bl[i],bl[e[j].to]);
}
void solve()
{
for(int i=1;i<=scc;i++) {
if(!head[i]) ans++;
}
}
int main()
{
scanf("%d",&n);
for(int id,i=1;i<=n;i++) {
scanf("%d",&id);
add_edge(id,i);
}
fo(i,1,n)if(!dfn[i])tarjan(i);
rebuild();
solve();
printf("%d\n",ans-1);
return 0;
}
对于联通块还有一种数据结构可以维护:并查集
最后统计答案时,就是每个点的所属有多少个不同编号,或者是有多少点的编号不变(两者是等价的,但后者快一点)(1720ms,1940ms)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+10;
int fa[N],n,len=0,vis[N],ans=0;
struct Edge{int from,to;Edge(int from=0,int to=0):from(from),to(to){}}e[N];
void add_edge(int u,int v){e[++len]=Edge(u,v);}
int Fd(int x){return x==fa[x]?fa[x]:fa[x]=Fd(fa[x]);}
void solve()
{
fo(i,1,n) fa[i]=i;
for(int i=1;i<=n;i++) {
int idx=Fd(e[i].from),idy=Fd(e[i].to);
if(idx==idy)continue;
fa[idx]=idy;
}
fo(i,1,n) if(!vis[Fd(i)]){ans++;vis[Fd(i)]=1;}
//或者 fo(i,1,n) if(fa[i]==i) ans++;
}
int main()
{
scanf("%d",&n);
for(int id,i=1;i<=n;i++) {
scanf("%d",&id);
add_edge(id,i);
}
solve();
printf("%d\n",ans);
return 0;
}
该博客探讨了如何解决bzoj1529问题,即计算联通块的数量。文章提到,尽管可以使用Tarjan算法进行缩点来统计,但在面对内存限制时可能会遇到问题。因此,作者提出了使用并查集作为替代方法来有效维护联通块。在最终统计答案时,通过计算点的所属联通块数量或者不变的点数(两者等价)来得出结果。实现中,使用并查集的方法在效率上表现出色,分别用时1720ms和1940ms。
551

被折叠的 条评论
为什么被折叠?



