【洛谷3119】[USACO15JAN] Grass Cownoisseur G(Tarjan+SPFA)

点此看题面

  • 给定一张(n)个点(m)条边的有向图。
  • (1)号点出发走回(1)号点,可以走至多一次反向边,问最多能经过多少节点。
  • (n,mle10^5)

(Tarjan)

不考虑可以走反向边这一条件,那么答案就是(1)号点所在强连通分量的点数。

众所周知强连通分量可以用(Tarjan)

其实也正是为了复习(Tarjan)我才会来打这道题的。。。

总而言之,我们先对整张图跑遍(Tarjan)缩一次点。

走反向边

考虑走反向边,必然是从(1)号点所在强连通分量向下走到某一个点,然后走反向边回到(1)号点之前的一个点,再走回到(1)号点所在的强连通分量。

我们枚举反向边((x,y)),令(u_i)(1)号点到其后面每个点的最长路,(v_i)(1)号点到其前面每个点的最长路,那么答案就是最大的(Sz_{c_1}+u_x+v_y)

想了半天不太清楚怎么从(DAG)中间开始拓扑。。。

于是只好暴力地写了个(SPFA)求最长路了。

代码:(O(SPFA))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,m;
struct Graph
{
	int ee,lnk[N+5];struct edge {int to,nxt;}e[N+5];
	I void add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y;}
	I int operator [] (CI x) Con {return lnk[x];}
	I edge operator () (CI x) Con {return e[x];}
}G,nG,iG;
namespace Tarjan//Tarjan缩点
{
	int d,dfn[N+5],low[N+5],T,S[N+5],IS[N+5],cnt,c[N+5],Sz[N+5];
	I void dfs(CI x)
	{
		dfn[x]=low[x]=++d,IS[S[++T]=x]=1;for(RI i=G[x];i;i=G(i).nxt) dfn[G(i).to]?
			IS[G(i).to]&&Gmin(low[x],dfn[G(i).to]):(dfs(G(i).to),Gmin(low[x],low[G(i).to]));
		if(dfn[x]==low[x]) {++cnt;W(++Sz[c[S[T]]=cnt],IS[S[T]]=0,S[T--]^x);}
	}
}
namespace SPFA//SPFA求最长路
{
	int IQ[N+5];queue<int> q;
	int u[N+5],v[N+5];I void Work(Con Graph& G,int* dis,CI x)
	{
		RI i,k,y,t;dis[x]=0,q.push(x);W(!q.empty())
			for(i=G[k=q.front()],q.pop(),IQ[k]=0;i;i=G(i).nxt) y=G(i).to,
				dis[y]<(t=dis[k]+Tarjan::Sz[y])&&(dis[y]=t,!IQ[y]&&(q.push(y),IQ[y]=1));
	}
}
int main()
{
	using namespace Tarjan;using namespace SPFA;
	RI i,j,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),G.add(x,y);
	for(i=1;i<=n;++i) !dfn[i]&&(dfs(i),0);for(i=1;i<=n;++i) for(j=G[i];//建新图
		j;j=G(j).nxt) c[i]^c[G(j).to]&&(nG.add(c[i],c[G(j).to]),iG.add(c[G(j).to],c[i]),0);
	memset(u,-1,sizeof(u)),memset(v,-1,sizeof(v)),Work(nG,u,c[1]),Work(iG,v,c[1]);//跑两侧最长路
	RI t=Sz[c[1]];for(i=1;i<=cnt;++i) if(~u[i])
		for(j=iG[i];j;j=iG(j).nxt) ~v[iG(j).to]&&Gmax(t,Sz[c[1]]+u[i]+v[iG(j).to]);//枚举反向边统计答案
	return printf("%d
",t),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3119.html