洛谷P3119 [USACO15JAN]Grass Cownoisseur G tarjan+spfa

題目鏈接:https://www.luogu.com.cn/problem/P3119

首先肯定是要用tarjan縮點的,這樣圖上就不會有環了,你所需做的就是加一條邊的反向邊,使這張圖能有以 1 爲起點 和 終點的環並且這個環要儘可能大。我在洛谷的題解裏看到一個非常巧妙的做法,我們可以將縮完點的圖複製一下,形成上下兩層相同的圖,下層每個點的編號是上層每個點編號+col_id(強連通分量的數量),然後上層對於每條從 v 到 u 的邊,加一條從 u 到 v+col_id 的邊。例如下圖
在這裏插入圖片描述
我們每次走紅邊,就不可能回到黑邊,所以走紅邊就相當於一次逆行。最後我們只需跑一次以 col[1] (包含1的強連通分量) 爲起點的spfa即可,答案就是 1 到 1’ 的距離。
代碼如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=3e5+5;
struct node
{
    int to,next;
}edge1[maxn],edge2[maxm];
int cnt1,cnt2;
int head1[maxn],head2[maxm];
int stc[maxn],dfn[maxn],low[maxn];
int col[maxn];
int col_num[maxm];
bool vis[maxn];
int top,dfn_num,col_id;
void add1(int x,int y)
{
    edge1[++cnt1].next=head1[x];
    edge1[cnt1].to=y;
    head1[x]=cnt1;
}
void add2(int x,int y)
{
    edge2[++cnt2].next=head2[x];
    edge2[cnt2].to=y;
    //edge2[cnt2].w=c;
    head2[x]=cnt2;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++dfn_num;
    stc[++top]=u;
    vis[u]=1;
    for(int i=head1[u];i;i=edge1[i].next)
    {
        int v=edge1[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
        low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        vis[u]=false;
        col[u]=++col_id;
        int len=1;
        while(stc[top]!=u)
        {
            len++;
            vis[stc[top]]=false;
            col[stc[top--]]=col_id;
        }
        top--;
        col_num[col_id]=len;
    }
}
int dis[maxm];
queue<int>q;
bool tag[maxm];
int n,m;
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        add1(x,y);
    }
    for(int i=1;i<=n;i++)
    if(!dfn[i])
    tarjan(i);
    for(int i=1;i<=col_id;i++)
    col_num[i+col_id]=col_num[i];
    for(int i=1;i<=n;i++)
    for(int j=head1[i];j;j=edge1[j].next)
    {
        int v=edge1[j].to;
        if(col[i]!=col[v])//建新圖
        {
            add2(col[i],col[v]);
            add2(col[i]+col_id,col[v]+col_id);
            add2(col[v],col[i]+col_id);
        }
    }
    tag[col[1]]=1;
    q.push(col[1]);
    while(!q.empty())
    {
        int u=q.front();
        for(int i=head2[u];i;i=edge2[i].next)
        {
            int v=edge2[i].to;
            if(dis[v]<dis[u]+col_num[u])
            {
                dis[v]=dis[u]+col_num[u];
                if(!tag[v])
                {
                    tag[v]=1;
                    q.push(v);
                }
            }
        }
        q.pop();
        tag[u]=0;
    }
    printf("%d\n",dis[col[1]+col_id]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章