【縮點】解題報告:luogu P2746 [USACO5.3]校園網Network of Schools(有向圖、強連通分量、縮點)

題目鏈接:P2746 [USACO5.3]校園網Network of Schools
在這裏插入圖片描述
用tarjan算法求出強連通分量,並且縮點,如果縮點後只有一個點,則答案爲1,0

對於第一問,如果縮點後某一點的入度爲0,則它肯定要一份軟件,如果不爲0,則一定可以從別的點獲得軟件,而對於下一個問題,縮點後,對每個出度爲0的點,需拓展一條出邊,對每個入度爲0的點,需拓展一條入邊,則可以完成條件。所以爲了拓展的邊更少,可以從出度爲0的點向入度爲0的點拓展邊。所以所需最少的拓展數即出度爲0的點數和入度爲0的點數的較大者。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<queue>
//#define ls (p<<1)
//#define rs (p<<1|1)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
//#define lowbit(p) p&(-p)
using namespace std;

typedef long long ll;
typedef pair<int,int> PII;
const ll INF = 1e18;
const int N = 5e5+7;
const int M = 5e5+7;

int head[N],nex[M],ver[M],tot = 1;
//vector<int>scc[N];
int cnt,n,m;
int dfn[N],low[N],num;
int stk[N],top;
int vis[N],c[N];
int in[N],out[N],ansi,anso;
int ins[N];//判斷是否在棧中

inline void read(int &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}

void add(int x,int y){
    ver[++tot] = y;nex[tot] = head[x];head[x] = tot;
}

void tarjan(int x){
    dfn[x] = low[x] = ++num;
    stk[++top] = x,ins[x] = 1;
    for(int i = head[x];i;i = nex[i]){
        int y = ver[i];
        if(!dfn[y]){
            tarjan(y);
            low[x] = min(low[x],low[y]);
        }
        else if(ins[y])
            low[x] = min(low[x],dfn[y]);
    }
    if(dfn[x] == low[x]){
        cnt++;
        int y;
        do{
            y = stk[top--];
            ins[y] = 0;//已經出棧
            c[y] = cnt;
            //scc[cnt].push_back(y);
        }while(x != y);
    }
}
int x;
int main(){
    cin>>n;
    over(i,1,n){
        scanf("%d",&x);
        while(x != 0){
            add(i,x);
            scanf("%d",&x);
        }
    }
    over(i,1,n)
    if(!dfn[i])
        tarjan(i);
    over(i,1,n)
        for(int j = head[i];j;j = nex[j]){
            int k = ver[j];
            if(c[k] != c[i]){
                in[c[k]]++;//已經縮過點了,不能用原來的那一套了
                out[c[i]]++;
            }
        }
    over(i,1,cnt){//已經縮過點了,要看cnt
        if(!in[i])ansi++;
        if(!out[i])anso++;
    }
    if(cnt == 1){cout<<1<<endl<<0;return 0;}
    cout<<ansi<<endl;
    cout<<max(anso,ansi);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章