洛谷 3119 [USACO15JAN]草鉴定Grass Cownoisseur 题解(强连通分量,思维)

原题链接:
洛谷

题意简述

B e s s i e Bessie Bessie要去旅行,在一个有向图上,从点 1 1 1开始。她想逆行一次,求她最多能经过多少个不同的草堆。

数据

输入格式
n m
//点数,边数(n,m<=100000)
u v
u v
...
u v
///m行,每行的u,v表示一条有向边从u到v(1<=u,v<=n)
输出格式
ans
//一行,答案
样例

输入

7 10 
1 2 
3 1 
2 5 
2 4 
3 7 
3 5 
3 6 
6 5 
7 2 
4 7 

输出

6

思路

首先,我们发现,要求最大化不同的点数量。
不同相当于什么?经过第二次不算。看到这个,自然而然就想到了强连通分量。
所以,无论如何,先缩个点。
珂是然后呢?
S C C i d [ i ] SCCid[i] SCCid[i]表示 i i i的强连通分量编号
d i s [ i ] dis[i] dis[i]表示从 S C C i d [ 1 ] SCCid[1] SCCid[1] S C C i d [ i ] SCCid[i] SCCid[i]的最长路。 i d i s [ i ] idis[i] idis[i]表示从 S C C i d [ i ] SCCid[i] SCCid[i] S C C i d [ 1 ] SCCid[1] SCCid[1]的最长路。那么我们只要枚举缩后的点 u u u,枚举 u u u的出点 v v v,用 d i s [ u ] + i d i s [ v ] − s i z e [ S C C i d [ 1 ] ] dis[u]+idis[v]-size[SCCid[1]] dis[u]+idis[v]size[SCCid[1]]即珂。

Q:为啥要减掉那个东西?
A:去的路上和回来的路上, 1 1 1所在的强连通分量都被算了一次,所以要减掉。

然后 d i s dis dis都会求的,就是 S P F A SPFA SPFA跑一遍最长路即珂。
那么 i d i s idis idis怎么求呢?仔细想想,我们只要建个反图,同样的方法跑一遍 S P F A SPFA SPFA即珂。

代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define N 200100
    class Graph
    {
        public:
            int *head;
            int EdgeCount;
            struct Edge
            {
                int To,Label,Next;
            }*Ed;
            int MAX;

            void SET(int len)
            {
                MAX=(len<<1)+100;
                Ed=new Edge[MAX];
                head=new int[MAX];

                memset(head,-1,sizeof(int)*MAX);
                memset(Ed,-1,sizeof(Edge)*MAX);
                EdgeCount=0;
            }
            void clear()
            {
                memset(head,-1,sizeof(int)*MAX);
                memset(Ed,-1,sizeof(Edge)*MAX);
                EdgeCount=0;
            }

            void AddEdge(int u,int v,int w)
            {
                ++EdgeCount;
                Ed[EdgeCount]=(Edge){v,w,head[u]};
                head[u]=EdgeCount;
            }
            void Add2(int u,int v,int w)
            {
                AddEdge(u,v,w);AddEdge(v,u,w);
            }

            int Start(int u)
            {
                return head[u];
            }
            int To(int u)
            {
                return Ed[u].To;
            }
            int Label(int u)
            {
                return Ed[u].Label;
            }
            int Next(int u)
            {
                return Ed[u].Next;
            }

    }G;
    struct Edge//临时图
    {
        int u,v,w;
    }E[N<<1];int ecnt=0;void AddEd(int u,int v,int w){E[++ecnt]=(Edge){u,v,w};}

    int n,m;
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Input()
    {
        R1(n),R1(m);
        G.SET(m<<1);
        for(int i=1;i<=m;++i)
        {
            int u,v;
            R1(u),R1(v);
            G.AddEdge(u,v,1);
            AddEd(u,v,1);
        }
    }

    //normal
    int DFSid[N],low[N],time=0;
    bool In[N];stack<int>STK;
    int SCCid[N],SCCcnt=0;
    //addition
    int size[N];
    void Tarjan(int u)
    {
        DFSid[u]=low[u]=++time;
        STK.push(u);In[u]=1;
        for(int i=G.Start(u);~i;i=G.Next(i))
        {
            int v=G.To(i);
            if (!DFSid[v])
            {
                Tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if (In[v]) low[u]=min(low[u],low[v]);
        }
        if (DFSid[u]==low[u])
        {
            int top;
            ++SCCcnt;
            do
            {
                top=STK.top();STK.pop();
                In[top]=0;
                SCCid[top]=SCCcnt;

                ++size[SCCcnt];
            } while (top!=u);
        }
    }//【模板】强连通分量
    int dis[N],idis[N];
    bool vis[N];
    queue<int>Q,EmptyQ;
    void SPFA(int *d,int s)
    {
        Q=EmptyQ;
        memset(vis,0,sizeof(vis));
        Q.push(s);
        d[s]=size[s];
        while(!Q.empty())
        {
            int u=Q.front();Q.pop();
            for(int i=G.Start(u);~i;i=G.Next(i))
            {
                int v=G.To(i);
                if (d[u]+size[v]>d[v])
                {
                    d[v]=d[u]+size[v];
                    if (!vis[v])
                    {
                        vis[v]=1;
                        Q.push(v);
                    }
                }
            }
            vis[u]=0;
        }
    }//【模板】SPFA
    void DP()
    {
        int s=SCCid[1];
        SPFA(dis,s);
        G.clear();
        for(int i=1;i<=ecnt;++i)
        {
            int u=E[i].u,v=E[i].v;
            G.AddEdge(v,u,1);//注意这里是v,u,不能写反
        }//反着存图
        SPFA(idis,s);
    }
    void Soviet()
    {
        //tarjan
        time=SCCcnt=0;
        memset(size,0,sizeof(size));
        memset(DFSid,0,sizeof(DFSid));
        memset(low,0,sizeof(low));
        for(int i=1;i<=n;++i)
        {
            if (!DFSid[i])
            {
                Tarjan(i);
            }
        }

        //rebuild
        G.clear();
        for(int i=1;i<=ecnt;++i)
        {
            int u=E[i].u,v=E[i].v;
            if (SCCid[u]!=SCCid[v])
            {
                G.AddEdge(SCCid[u],SCCid[v],1);
            }
        }
        ecnt=0;memset(E,0,sizeof(E));
        for(int u=1;u<=SCCcnt;++u)
        {
            for(int i=G.Start(u);~i;i=G.Next(i))
            {
                int v=G.To(i);
                AddEd(u,v,1);
            }
        }


        DP();
        int ans=size[SCCid[1]];
        for(int u=1;u<=SCCcnt;++u)//注意:u<=SCCcnt!!!是枚举强连通分量!!!
        {
            for(int i=G.Start(u);~i;i=G.Next(i))
            {
                int v=G.To(i);//枚举出边
                if (dis[u] and idis[v])
                {
                    ans=max(ans,dis[u]+idis[v]-size[SCCid[1]]);
                    //注意减掉后面那个东西
                }
            }
        }
        printf("%d\n",ans);
    }
    void IsMyWife()
    {
        if (0)
        {
            freopen("download_testdatas/testdata.in","r",stdin);
//            freopen("","w",stdout);
			//本地调试用的。。。
        }
        Input();
        Soviet();
    }
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值