UPC 问题 E 交朋友(并查集+向前星+强连通分量)

  1. 把每个点指向的点合并成一个强连通分量并且他们任意两点有边,例如存在边,,,那么b、c、d合并成一个强连通分量
  2. 如果一个强连通分量里点的个数大于两个,那么这个强连通分量和他们的出边指向的点合并成一个强连通分量,例如存在边,,,,那么b、c、d、e合并成一个强连通分量
  3. 最后统计每个强连通里面的边数和强连通与强连通之间的边数
#include
typedef long long ll;
using namespace std;
const int maxn=200010;
int head[maxn],nume=1;
int n,m;
ll ans;
struct Edge
{
    int v;
    int next;
}e[maxn];
int fa[maxn],num[maxn];//num记录边的数量
void add_Edge(int u,int v)//向前星存储图
{
    e[nume].v=v;
    e[nume].next=head[u];
    head[u]=nume++;
}
int Find(int x)//并查集
{
    return fa[x]==-1?x:fa[x]=Find(fa[x]);
}
void dfs(int u,int x)
{
    for(int i=head[u];~i;i=e[i].next)
    {
        int y=e[i].v;
        x=Find(x);
        y=Find(y);
        if(x!=y)
        {
            fa[y]=x;
            num[x]+=num[y];
            dfs(y,x);
        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<=n;i++)
    {
        num[i]=1;
        fa[i]=head[i]=-1;
    }
    int u,v;
    for(int i=1;i<=m;i++)
    {
        cin>>u>>v;
        add_Edge(u,v);
    }
    bool update=true;
    while(update)
    {
        update=false;
        for(int j=1;j<=n;j++)
        {
            int x=num[Find(j)]==1?-1:j;
            for(int i=head[j];~i;i=e[i].next)
            {
                int y=e[i].v;
                if(x==-1) x=y;
                else
                {
                    x=Find(x);
                    y=Find(y);
                if(x!=y)
                {
                    fa[y]=x;
                    num[x]+=num[y];
                    update=true;
                    dfs(y,x);
                }
                }
            }
        }
    }
    for(int j=1;j<=n;j++)
    {
        if (fa[j]==-1) ans+=(ll)num[j]*(num[j]-1);
         for (int i=head[j];~i;i=e[i].next) {
            int x=Find(j),y=Find(e[i].v);
            if (x!=y) ans++;
        }
    }
    printf("%lld",ans);
    return 0;


}

你可能感兴趣的:(并查集)