问题描述
约翰有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
#include
#include
#define Min(x,y) ((x
#define Max(x,y) ((x>y)?(x):(y))
#define MAXN 100005
#define MAXM 200005
using 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]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]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]);//与起点在同一强连通分量的草也能吃到
}