原题链接:
洛谷
题意简述
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;
}