洛谷 P3119 [USACO15JAN]Grass Cownoisseur G 题解
Posted jiangtaizhe001
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 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;
}
以上是关于洛谷 P3119 [USACO15JAN]Grass Cownoisseur G 题解的主要内容,如果未能解决你的问题,请参考以下文章
洛谷 P3119 [USACO15JAN]草鉴定Grass Cownoisseur
洛谷 P3119 [USACO15JAN]Grass Cownoisseur G 题解
P3119 [USACO15JAN]草鉴定Grass Cownoisseur
P3119 [USACO15JAN]草鉴定Grass Cownoisseur