【USACO 2015 Jan Gold】强连通分量+最长路

来源:互联网 发布:知乎 性观念 编辑:程序博客网 时间:2024/05/01 10:59

NKOJ 3213牧草鉴赏家

问题描述

约翰有n块草场,编号1到n,这些草场由若干条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。

贝西总是从1号草场出发,最后回到1号草场。她想经过尽可能多的草场,贝西在通一个草场只吃一次草,所以一个草场可以经过多次。因为草场是单行道连接,这给贝西的品鉴工作带来了很大的不便,贝西想偷偷逆向行走一次,但最多只能有一次逆行。问,贝西最多能吃到多少个草场的牧草。

输入格式

第一行,两个整数N和M(1<=N,M<=100000)
接下来M行,表示有M条单向道路,每条道路有连个整数X和Y表示,从X出发到达Y。

输出格式

一个整数,表示所求答案


首先可以想到,处于同一个强连通分量的点,如果能够从1号点走到强连通分量中,那么就能够全部吃完。因此我们可以用Tarjan算法缩点。

缩完点之后,就变成了一个有向无环图。然而我们最后还是要回到1号点,所以就有两种情况:

1.只吃完与起点处在同一个强连通分量的所有草;
2.通过逆行回到起点。

主要讨论情况2。此时逆行的边 < a, b > 显然满足从起点能够到达a,且从b能够到达起点。那么为了解决“从b能够到达起点”,对原图建一个反图。分别对原图与反图跑一次最短路算法后,枚举逆边即可。

#include<stdio.h>#include<stack>#include<queue>#define Min(x,y) ((x<y)?(x):(y))#define Max(x,y) ((x>y)?(x):(y))#define MAXN 100005#define MAXM 200005using namespace std;int N,M,T,Size[MAXN],Ans;inline int _R(){    char s=getchar();int v=0,sign=0;    while((s!='-')&&(s>57||s<48))s=getchar();    if(s=='-')sign=1,s=getchar();    for(;s>47&&s<58;s=getchar())v=v*10+s-48;    if(sign)v=-v;    return v;}int tot,en[MAXM],nex[MAXM],las[MAXN];void ADD_scc(int x,int y){    en[++tot]=y;    nex[tot]=las[x];    las[x]=tot;}stack<int>S;int VT,scc,be[MAXN],dfn[MAXN],low[MAXN];bool mark[MAXN];void Tarjan(int x){    dfn[x]=low[x]=++VT;    mark[x]=true;    S.push(x);    int i,y;    for(i=las[x];i;i=nex[i])    {        y=en[i];        if(!dfn[y])        {            Tarjan(y);            low[x]=Min(low[x],low[y]);        }        else if(mark[y])low[x]=Min(low[x],dfn[y]);    }    if(dfn[x]!=low[x])return;    scc++;    do    {        y=S.top();S.pop();mark[y]=false;        be[y]=scc;    }while(y!=x);}int Tot,En[MAXM],Las[MAXN],Nex[MAXM],Len[MAXM],St[MAXM];bool inv[MAXM];void ADD(int x,int y,int z,int w){    En[++Tot]=y;    Nex[Tot]=Las[x];    Las[x]=Tot;    Len[Tot]=z;    St[Tot]=x;    inv[Tot]=w;}struct node{int p,al;};bool in[MAXN][2];int Dis[MAXN][2];void SPFA1(int s){    queue<int>Q;    int i,y,x,z;    for(i=1;i<=scc;i++)Dis[i][0]=-1e9;    Dis[s][0]=0;    Q.push(s);    while(Q.size())    {        x=Q.front();Q.pop();        in[x][0]=false;        for(i=Las[x];i;i=Nex[i])        {            y=En[i];            if(inv[i])continue;            if(Dis[y][0]<Dis[x][0]+Len[i])            {                Dis[y][0]=Dis[x][0]+Len[i];                if(!in[y][0]){in[y][0]=true;Q.push(y);}            }        }    }}//原图void SPFA2(int s){    queue<int>Q;    int i,y,x,z;    for(i=1;i<=scc;i++)Dis[i][1]=-1e9;    Dis[s][1]=0;    Q.push(s);    while(Q.size())    {        x=Q.front();Q.pop();        in[x][1]=false;        for(i=Las[x];i;i=Nex[i])        {            y=En[i];            if(!inv[i])continue;            if(Dis[y][1]<Dis[x][1]+Len[i])            {                Dis[y][1]=Dis[x][1]+Len[i];                if(!in[y][1]){in[y][1]=true;Q.push(y);}            }        }    }}//反图int main(){    int i,j,x,y;    scanf("%d%d",&N,&M);    for(i=1;i<=M;i++)    {        scanf("%d%d",&x,&y);        ADD_scc(x,y);    }    for(i=1;i<=N;i++)if(!dfn[i])Tarjan(i);    for(i=1;i<=N;i++)Size[be[i]]++;    for(x=1;x<=N;x++)    {        for(i=las[x];i;i=nex[i])        {            y=en[i];            if(be[x]==be[y])continue;            ADD(be[x],be[y],Size[be[y]],0);            ADD(be[y],be[x],Size[be[x]],1);        }    }    T=be[1];    SPFA1(T);    SPFA2(T);    for(i=1;i<=Tot;i++)    {        if(!inv[i])continue;        x=St[i];y=En[i];        if(Dis[x][0]!=-1e9&&Dis[y][1]!=-1e9)Ans=Max(Ans,(Dis[x][0]+Dis[y][1]));//枚举逆边,如果Dis为-1e9,说明到不了    }    printf("%d",Ans+Size[T]);//与起点在同一强连通分量的草也能吃到}
阅读全文
0 0