2022-2023 ICPC Brazil Subregional Programming Contest H(Targan缩点)

415 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

H - Helping the Transit

The president of Nlogonia decided, by decree, that all the streets of Nlogonia should be one-way. Due to the lack of knowledge of elementary science, there was no proper planning for the changes. After the new system came in place, people would not be able to go to work, or would not be able to return home from work, for example. As a result, there was chaos and riots in lots of cities.
The president was impeached and the new administration of the country hired a team of scientists to solve the problem. In turn, the committee hired you, an expert in complexity of algorithms, to help them with the efficient computation of solutions.
So, for each city, you are given the reference points of the city, and the one-way streets, each of which connects two reference points. Your task is to determine the minimum number of one-way bridges that must be built in order to have full connectivity in the city. Each bridge should also connect two reference points.

中文大意

给我们n个有向边问我们最少添加有向边让我们可以从任意一点去往其他所有的点

解法

如果几个有向边形成了一个环那么这个环中的一点就是去环中的所有点,所有我们使用Taijan缩点,然后统计缩点过后的出度和入度

#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
using ll = long long;
#define int long long
#define endl '\n'
#define pb push_back
#define NO cout << "NO" << endl;
#define YES cout << "YES" << endl;
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define rep(i,n) for(int i = 1; i <= n; i++)
typedef pair<int,int> PII;
const int N = 1e6 + 10;
int dfn[N],low[N],num,co[N],col;
int top,st[N];
int si[N];
int cd[N], rd[N];
vector<int> v[N];
void Tarjan(int u)///tarjan缩点
{
    dfn[u] = low[u] = ++ num;
    st[++top] = u;
    for(auto x : v[u])
    {
       if(!dfn[x]) { // 深度优先
        Tarjan(x);
        low[u] = min(low[u],low[x]);
       }else if(!co[x]) low[u] = min(low[u],dfn[x]);
       // 判断是否再栈中
    }
    if(low[u] == dfn[u]) { // 退栈
        co[u] = ++col; // 记录在第几个集合
        ++ si[col];//记录集合数量
        while(st[top] != u) {
            ++ si[col];
            co[st[top]] = col;
            top --;
        }
        top --;
    }
}
void solve()
{
   int n,m; cin >> n >> m;
   rep(i,m) {
   int x,y; cin >> x >> y;
   v[x].pb(y);
   }
   rep(i,n) if(!dfn[i]) Tarjan(i);//缩点
   rep(i,n) for(auto x : v[i])
   if(co[i] != co[x]) rd[co[x]] ++, cd[co[i]] ++;//统计入度
   int cnt1 = 0, cnt2 = 0;
   rep(i,col) {
       if(cd[i]) cnt1 ++;
       if(rd[i]) cnt2 ++;
   }
   if(col == 1) {
     cout << 0 << endl;
     return;
   }
   cout << col << endl;
   cout << col - min(cnt1, cnt2) << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    // int T;cin >> T;
    // while ( T -- )
    solve();
    return 0;
}