P3119 [USACO15JAN]草鉴定Grass Cownoisseur
显然每次走到一个联通块肯定要把整个联通块的草场都走一遍,考虑缩点
然后直接建分层图跑最长路就好了
(为了方便,以下的强连通分量均称为点)
但是有一个小问题,如果反着走可能走到以前走过的点,怎么判断(因为每个点只有一次贡献)
其实根本不用判断,因为如果从一号点出发,走出去后要走回来一定要逆行一次(因为没有环)
如果你把逆行机会用在走以前走过的点(出发点除外),那你就永远走不回出发点了
所以不会对答案产生贡献,然后就是Dijk跑最长路了
分层图十分显然,dis[ i ] [ 0/1 ] 表示到达点 i ,是否用过逆行机会时的最长路
建图时记得建一下反图就好了
// luogu-judger-enable-o2 #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<iostream> #include<vector> #include<queue> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e5+7; int n,m; int fir[N],from[N<<1],to[N<<1],cntt=0; inline void add(int &a,int &b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; } //以下tarjan模板 int dfn[N],low[N],dfs_clock,st[N],Top,be[N],tot,cnt[N]; void Tarjan(int x) { dfn[x]=low[x]=++dfs_clock; st[++Top]=x; for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(!dfn[v]) Tarjan(v),low[x]=min(low[x],low[v]); else if(!be[v]) low[x]=min(low[x],dfn[v]); } if(low[x]==dfn[x]) { tot++; cnt[tot]=1; while(st[Top]!=x) { cnt[tot]++; be[st[Top--]]=tot; } be[st[Top--]]=tot; } } vector <int> v1[N],v2[N];//为了方便直接用vector存图,v1是正图,v2是反图 void build()//建图 { for(int i=1;i<=n;i++) for(int j=fir[i];j;j=from[j]) { int &v=to[j]; if(be[i]==be[v]) continue; v1[be[i]].push_back(be[v]); v2[be[v]].push_back(be[i]); } } int dis[N][2]; struct data { int pos,dis,p;//当前位置,经过距离,是否逆行过 inline bool operator < (const data &tmp) const { return dis<tmp.dis; } }; priority_queue <data> q; void Dijk() { memset(dis,128,sizeof(dis)); q.push((data){be[1],0,0}); dis[be[1]][0]=0;//初始状态 data x; while(!q.empty()) { x=q.top(); q.pop(); if(dis[x.pos][x.p]!=x.dis) continue; int len=v1[x.pos].size(); for(int i=0;i<len;i++) { int &v=v1[x.pos][i]; if(dis[v][x.p]<x.dis+cnt[v]) dis[v][x.p]=x.dis+cnt[v],q.push((data){v,dis[v][x.p],x.p}); } if(!x.p)//如果没逆行过就考虑逆行 { len=v2[x.pos].size(); for(int i=0;i<len;i++) { int &v=v2[x.pos][i]; if(dis[v][1]<x.dis+cnt[v]) dis[v][1]=x.dis+cnt[v],q.push((data){v,dis[v][1],1}); } } } } int main() { int a,b; n=read(); m=read(); for(int i=1;i<=m;i++) { a=read(); b=read(); add(a,b); } for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);//记得每个点都要缩起来,正图走不通可能反图能走 if(tot==1) { printf("%d",n); return 0; }//记得特判一波 build(); Dijk(); printf("%d",max(dis[be[1]][0],dis[be[1]][1])); return 0; }