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

  1. 把每个点指向的点合并成一个强连通分量并且他们任意两点有边,例如存在边<a,b>,<a,c>,<a,d>,那么b、c、d合并成一个强连通分量
  2. 如果一个强连通分量里点的个数大于两个,那么这个强连通分量和他们的出边指向的点合并成一个强连通分量,例如存在边<a,b>,<a,c>,<a,d>,<d,e>,那么b、c、d、e合并成一个强连通分量
  3. 最后统计每个强连通里面的边数和强连通与强连通之间的边数
#include<bits/stdc++.h>
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;


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值