洛谷 P3119 [USACO15JAN]Grass Cownoisseur G 题解

题目传送门
题目大意:有一个有向图,从 \(1\) 号点出发,可以走一次反向边(“逆行”),当然也可以不走,可以重复经过点。求从 \(1\) 号点出发回到 \(1\) 号点最多能经过几个点。

前置知识:如何用线性算法求一张有向图的强连通分量

显然我们发现,如果 \(x\) 号点能到达,那么显然和 \(x\) 点位于同一个强连通分量内的点都可以到达,那么显然我们需要先缩点。
缩点之后就变成了一张DAG图。由于可以逆行一次,所以我们可以考虑分层图,但是其实不需要用分层图。
我们把所有的点进行标号,分成三类点: \(1\) 号点, \(1\) 号点能到达的点,能到达 \(1\) 号点的点。分别记作 \(1\) 号点,I类点,II类点。
显然对于任何一条合法的路径都有三段: \(1\) 号点->I类点->II类点-> \(1\) 号点
我们可以使用建反向图+SPFA的方法求出 \(1\) 号点到每个I类点最多能经过几个点,每个II类点到 \(1\) 号点最多能经过几个点。由于只能走一次反向边,所以我们只要枚举每一条反向边从而计算答案。

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define maxn 100039
#define maxm 200039
using namespace std;
//#define debug
typedef int Type;
inline Type read(){
	Type sum=0;
	int flag=0;
	char c=getchar();
	while((c<'0'||c>'9')&&c!='-') c=getchar();
	if(c=='-') c=getchar(),flag=1;
	while('0'<=c&&c<='9'){
		sum=(sum<<1)+(sum<<3)+(c^48);
		c=getchar();
	}
	if(flag) return -sum;
	return sum;
}
int n,m,u,v;
int head[maxn],nex[maxm],to[maxm],kkk;
#define add(x,y) to[++kkk]=y;\
nex[kkk]=head[x];\
head[x]=kkk;
struct Stack{
	int data[maxn],_top;
	inline int top(){ return data[_top]; }
	inline void push(int x){ data[++_top]=x; return; }
	inline void pop(){ _top--; return; }
	inline bool empty(){ return _top<=0; }
}s;
int dfn[maxn],low[maxn],cnt,scc[maxn],siz[maxn],num,vis[maxn];
void dfs(int x){
	s.push(x); vis[x]=1;
	low[x]=dfn[x]=++cnt;
	for(int i=head[x];i;i=nex[i]){
		if(!dfn[to[i]]) dfs(to[i]);
		if(vis[to[i]]) low[x]=min(low[x],low[to[i]]);
	}
	if(low[x]==dfn[x]){
		vis[x]=0; scc[x]=++num;
		while(s.top()!=x){
			vis[s.top()]=0;
			scc[s.top()]=num;
			s.pop();
		}
		s.pop();
	}
	return;
}
void Tarjan(){
	for(int i=1;i<=n;i++)
	    if(!dfn[i]) dfs(i);
	return;
}
struct Graph{
	int _head[maxn],_nex[maxm],_to[maxm],_kkk;
	inline void _add(int x,int y){
		_to[++_kkk]=y;
		_nex[_kkk]=_head[x];
		_head[x]=_kkk;
		return;
	}
}a,b;
int f1[maxn],f2[maxn],scc1;
//flag[i]=1 1->i flag[i]=2 i->1
queue<int> q,E;
void SPFA(Graph a,int f[]){
	memset(vis,0,sizeof(vis));
	q=E; q.push(scc[1]); f[scc[1]]=siz[scc[1]];
	while(!q.empty()){
		int cur=q.front(); q.pop();
		for(int i=a._head[cur];i;i=a._nex[i]){
			if(f[cur]+siz[a._to[i]] > f[a._to[i]]){
				f[a._to[i]]=max(f[a._to[i]],f[cur]+siz[a._to[i]]);
			    if(!vis[a._to[i]]) q.push(a._to[i]);
			    vis[a._to[i]]=1;
			}
		}
		vis[cur]=0; 
	}
	return;
}
int main(){
	#ifndef ONLINE_JUDGE
    //freopen("P3119_3.in","r",stdin);
    //freopen("1.out","w",stdout);
    #endif
    n=read(); m=read();
    for(int i=1;i<=m;i++){
    	u=read(); v=read();
    	add(u,v);
	}
	Tarjan();
	for(int i=1;i<=n;i++) siz[scc[i]]++;
	if(siz[scc[1]]==n){
		printf("%d",n);
		return 0;
	}
	for(int i=1;i<=n;i++){
		for(int j=head[i];j;j=nex[j])
		    if(scc[i] != scc[to[j]]){
		    	a._add(scc[i],scc[to[j]]);
		    	b._add(scc[to[j]],scc[i]);
			}
	}
	scc1=scc[1]; SPFA(a,f1); SPFA(b,f2);
	int ans=siz[scc[1]];
	for(int i=1;i<=num;i++)
		if(f1[i])
	    	for(int j=b._head[i];j;j=b._nex[j])
	        	if(f2[b._to[j]])
					ans=max(ans,f1[i]+f2[b._to[j]]-siz[scc[1]]);
	printf("%d",ans);
	return 0;
}
posted @ 2021-06-14 02:40  jiangtaizhe001  阅读(66)  评论(0编辑  收藏  举报