B - Proving Equivalences HDU - 2767 (强连通分量+缩点+加边形成强连通图)

题意:给出n个定理,m个推导关系(单向),求至少加多少个推导关系,可以使得任意两个定理之间可以相互推导出来

思路:

  • 任意两个定理之间可以相互推导,即要构造一个强连通图,使得任意两点之间可以相互到达
  • 求强连通分量,缩点(同一个分量之中的定理可以两两推导出来了)
  • 接下来求加多少条边,可以使得这个DAG图变成强连通图。统计每个分量的出度和入度,若出度为0的点有x个,入度为0的点有y个,则至少要加max(x,y)条边即可。(这里求的也是出入度为0的点,出入度为0才有意义,对于出度不为0的分量来说,它的出度是包括重边的;入度也是,没多大意义;)
//#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<stack>
#include<vector>
#define rep(i,e) for(int i=0;i<(e);++i)
#define rep1(i,e) for(int i=1;i<=(e);++i)
#define repx(i,x,e) for(int i=(x);i<=(e);++i)
#define pii pair<int,int>
#define X first
#define Y second
#define PB push_back
#define MP make_pair
#define mset(var,val) memset(var,val,sizeof(var))
#define scd(a) scanf("%d",&a)
#define scdd(a,b) scanf("%d%d",&a,&b)
#define scddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define IOS ios::sync_with_stdio(false);cin.tie(0)

using namespace std;

#ifdef LOCAL
template<typename T>
void dbg(T t){
    cout<<t<<" "<<endl;
}
template<typename T, typename... Args>
void dbg(T t, Args... args){
    cout<<t<<" ";dbg(args...);
}


#else
#define dbg(...)
#endif // local
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
const int mod = 1e9+7;
const int N = 500+10;
typedef long long ll;
const int maxn = 2e4+10;
const int maxm = 5e4+10;

struct node{
    int v,next;
}edge[maxm];
int head[maxn];
int low[maxn],dfn[maxn],sccno[maxn],dfn_cnt,scc_cnt;
stack<int>S;
int cnt;
int ru[maxn],chu[maxn];
void add(int u,int v)
{
    edge[++cnt].v = v;
    edge[cnt].next = head[u];
    head[u] = cnt;
}

void tarjan(int u)
{
    low[u] = dfn[u] = ++dfn_cnt;
    S.push(u);
    for(int i = head[u]; i != 0; i = edge[i].next)
    {
        int v = edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }else if(!sccno[v])
        {
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(dfn[u] == low[u])
    {
        scc_cnt++;
        for(;;){
            int x = S.top();S.pop();
            sccno[x] = scc_cnt;
            if(x == u) break;
        }
    }
}

void work()
{
    int t,m,n,x,y;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i = 0; i <= n; i++)
        {
            low[i] = dfn[i] = sccno[i] = head[i] = ru[i] = chu[i] = 0;
            edge[i].next = 0;
        }
        cnt = scc_cnt = dfn_cnt = 0;
        for(int i = 0; i < m; i++)
        {
            cin>>x>>y;
            add(x,y);
        }
        for(int i = 1; i <= n; i++)
            if(!dfn[i])
                tarjan(i);
        if(scc_cnt == 1) 
        {
            cout<<0<<endl;
            continue;
        }
        for(int i = 1; i <= n; i++)
        {
            for(int j = head[i]; j != 0; j = edge[j].next)
            {
                int v = edge[j].v;
                if(sccno[i] != sccno[v])
                {
                    ru[sccno[v]]++;
                    chu[sccno[i]]++;
                }
            }
        }
        int r,c;
        r = c = 0;
        for(int i = 1; i <= scc_cnt; i++)
        {
            if(!ru[i]) r++;
            if(!chu[i]) c++;
        }
        cout<<max(r,c)<<endl;
    }
}

int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif
    IOS;
    work();
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值