AcWing 368. 銀河 (有向圖的強連通分量)

整理的算法模板:ACM算法模板總結(分類詳細版)

銀河中的恆星浩如煙海,但是我們只關注那些最亮的恆星。

我們用一個正整數來表示恆星的亮度,數值越大則恆星就越亮,恆星的亮度最暗是 1。

現在對於 N 顆我們關注的恆星,有 M 對亮度之間的相對關係已經判明。

你的任務就是求出這 N 顆恆星的亮度值總和至少有多大。

輸入格式

第一行給出兩個整數 N 和 M。

之後 M 行,每行三個整數 T, A, B,表示一對恆星(A, B)之間的亮度關係。恆星的編號從 1 開始。

如果 T = 1,說明 A 和 B 亮度相等。
如果 T = 2,說明 A 的亮度小於 B 的亮度。
如果 T = 3,說明 A 的亮度不小於 B 的亮度。
如果 T = 4,說明 A 的亮度大於 B 的亮度。
如果 T = 5,說明 A 的亮度不大於 B 的亮度。

輸出格式

輸出一個整數表示結果。

若無解,則輸出 -1。

數據範圍

N≤100000,M≤100000N≤100000,M≤100000

輸入樣例:

5 7 
1 1 2 
2 3 2 
4 4 1 
3 4 5 
5 4 5 
2 3 5 
4 5 1 

輸出樣例:

11

首先這道題可以用差分約束系統,用棧優化spfa求正環來做;但是,不是正解(畢竟棧優化spfa不是每次都可);

有向圖的強連通分量做法:

  1. 用tarjan求強連通分量跑圖,同時記錄每個連通分量的大小;
  2. 遍歷每一個點,然後對於不是一個強連通塊內的點進行縮點,建立新邊;
  3. 由於處理完的圖是有向無環圖,滿足拓撲序,那麼可以直接用遞推求最長路(因爲強連通分量的標號是在回溯的過程中進行的,是逆拓撲序,所以應該倒着遞推);
  4. 每個強連通塊內部兩兩之間值邊權一定爲0,否則會出現正環,也就是無解的情況;

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,M=N*6;
int n,m;
int h[N],hs[N],e[M],ne[M],w[M],idx;
int dfn[N],low[N],timestamp;
int stk[N],top;
bool in_stk[N];
int id[N],scc_cnt,size_scc[N];
int dis[N];
void add(int h[],int a,int b,int c)
{
    //cout <<a<<" "<<b<<" "<<c<<endl;
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void tarjan(int u)
{
    dfn[u] = low[u] = ++ timestamp;
    stk[ ++ top] = u, in_stk[u] = true;

    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (in_stk[j]) low[u] = min(low[u], dfn[j]);
    }

    if (dfn[u] == low[u])
    {
        ++ scc_cnt;
        int y;
        do {
            y = stk[top -- ];
            in_stk[y] = false;
            id[y] = scc_cnt;
            size_scc[scc_cnt] ++ ;
        } while (y != u);
    }
}
int main()
{
    cin >>n>>m;
    memset(h,-1,sizeof h);
    memset(hs,-1,sizeof hs);
    for(int i=1;i<=n;i++) add(h,0,i,1);
    while (m -- )
    {
        int t, a, b;
        cin >>t>>a>>b;
        if (t == 1) add(h, b, a, 0), add(h, a, b, 0);
        else if (t == 2) add(h, a, b, 1);
        else if (t == 3) add(h, b, a, 0);
        else if (t == 4) add(h, b, a, 1);
        else add(h, a, b, 0);
    }
    for(int i=0;i<=n;i++)
    {
        if(!dfn[i]) tarjan(i);
    }
    bool f=true;
    for(int i=0;i<=n;i++)
    {
        for(int j=h[i];~j;j=ne[j])
        {
            int k=e[j];
            int a=id[i],b=id[k];
            if(a!=b)
            {
                add(hs,a,b,w[j]);
            }
            else
            {
                if(w[j]>0)
                {
                    f=false;
                    break;
                }
            }
        }
        if(!f) break;
    }
    //for(int i=1;i<=n;i++) cout <<id[i]<<" ";
    if(!f) puts("-1"),exit(0);
    for(int i=scc_cnt;i;i--)
    {
        for(int j=hs[i];~j;j=ne[j])
        {
            int k=e[j];
            dis[k]=max(dis[k],dis[i]+w[j]);
        }
    }
    ll ans=0;
    for(int i=1;i<=scc_cnt;i++) ans+=(ll)dis[i]*size_scc[i];
    cout <<ans<<endl;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章