传送门:https://www.luogu.org/problemnew/show/P3119
这个题目一眼看上去,有环,我们就直接tarjan缩点。
但是有一条边可以逆着走,很显然的思路就是枚举每条边,记录一个最大值。(我不会写枚举边的暴力)
所以我们来介绍一下另外一种思路,建分层图。
现在我们在缩点之后的DAG上操作。
我们在DAG上建图的时候,用下图的方式建:
DAG结点为1,2,3,多加出来的点为4,5,6,我们就把2连到4,3连到5,这样,2到4就相当于2到1,也就是1到2的反边。
具体怎么建的,就是add(from,to),add(to,from+N),add(from+N,to+N);
这样,我们就把分层图给建好啦。
哦对了,权值别忘记更新了,这个题目是点权,没关系,把他当成边权就可以了。
然后在分层图上从1跑spfa最长路,因为最后必须回到1,所以结果就是dis[color[1]+N],因为1号结点对应的是1+N结点。
下面是大家喜闻乐见的代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+7;
int low[maxn],dfn[maxn];
vector<int> E[maxn];
vector<int> G[maxn*2];
int color[maxn],sum[maxn*2];
int n,m;
int cnt,col;
stack<int> s;
int ins[maxn];
int inq[maxn];
int dis[maxn];
void tarjan(int x)
{
low[x] = dfn[x] = ++cnt;
s.push(x);
ins[x] = 1;
for(int i=0;i<E[x].size();i++)
{
int v = E[x][i];
if(!dfn[v])
{
tarjan(v);
low[x] = min(low[x],low[v]);
}
else if(ins[v])
{
low[x] = min(low[x],dfn[v]);
}
}
if(low[x]==dfn[x])
{
++col;
while(true)
{
int tmp = s.top();
s.pop();
sum[color[tmp]=col]++;
ins[tmp] = 0;
if(tmp==x) break;
}
}
}
void spfa(int s)
{
queue<int> q;
q.push(s);
inq[s] = 1;
while(!q.empty())
{
int now = q.front();
q.pop();
inq[now] = 0;
for(int i=0;i<G[now].size();i++)
{
int v = G[now][i];
if(dis[v]<dis[now]+sum[v])
{
dis[v] = dis[now]+sum[v];
if(!inq[v])
{
inq[v] = 1;
q.push(v);
}
}
}
}
}
int main()
{
cin>>n>>m;
int x,y;
for(int i=0;i<m;i++)
{
cin>>x>>y;
E[x].push_back(y);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i]) tarjan(i);
}
if(col==1)
{
cout<<sum[color[1]]<<endl;
return 0;
}
for(int i=1;i<=col;i++)
{
sum[i+col] = sum[i];
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<E[i].size();j++)
{
int v = E[i][j];
if(color[i]!=color[v])
{
G[color[i]].push_back(color[v]);
G[color[v]].push_back(color[i]+col);
G[color[i]+col].push_back(color[v]+col);
}
}
}
spfa(color[1]);
cout<<dis[color[1]+col]<<endl;
return 0;
}