题目大意
约翰有n块草场,编号1到n,这些草场由若m条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。(1 <= n, m <= 100,000).
贝西总是从1号草场出发,最后回到1号草场。她想经过尽可能多的草场,贝西在通过一个草场时只能吃一次草,但一个草场可以经过多次。因为草场是单行道连接,这给贝西的品鉴工作带来了很大的不便,贝西想偷偷逆向行走一次,但最多只能有一次逆行。问,贝西最多能吃到多少个草场的牧草。
题目分析
观察“在通过一个草场时只能吃一次草,但一个草场可以经过多次”,很明显可以看出我们要把相互可以到达的点缩成一个点,这里用Tarjan实现即可。
缩点后建出新图,这时从 点1所在强连通分量 所开始跑一遍最短(长)路即可得出没有“逆行”这个条件时的答案。所得结果即为dis1[N]。
考虑如何处理逆行。因为贝西最后要回到1号草场,所以我们不妨再反向建一次边,得出以 点1所在强连通分量 为终点的所有最短(长)路。所得结果即为dis2[N]。
然后我们就可以枚举一个 强连通分量x 通过反边找出它可以逆行一次到达的 强连通分量 y, 然后就可以更新答案了。
注意 点1所在强连通分量 在作为起点和终点时都被计算了一次,所以要减掉 点1所在强连通分量 内的点数。
化为代码,就是 ans = max( ans, dis1[now] + dis2[y] - tot[bcc[1]] );
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=1e5+10; 4 const int MAXM=1e5+10; 5 6 struct Edge{ 7 int to,nxt; 8 }oe[MAXM],e1[MAXM],e2[MAXM]; 9 int cnt,cnt1,cnt2; 10 int head[MAXN],head1[MAXN],head2[MAXN]; 11 inline void add_edge(int u,int v){oe[++cnt].to=v;oe[cnt].nxt=head[u];head[u]=cnt;} 12 inline void add_edge1(int u,int v){e1[++cnt1].to=v;e1[cnt1].nxt=head1[u];head1[u]=cnt1;} 13 inline void add_edge2(int u,int v){e2[++cnt2].to=v;e2[cnt2].nxt=head2[u];head2[u]=cnt2;} 14 15 int n,m; 16 17 int col,c[MAXN],bcc[MAXN],tot[MAXN]; 18 int tim,dfn[MAXN],low[MAXN]; 19 int top,st[MAXN]; 20 bool insta[MAXN]; 21 22 bool vis[MAXN]; 23 int dis1[MAXN],dis2[MAXN]; 24 inline void Tarjan(int x){ 25 dfn[x]=low[x]=++tim; 26 st[++top]=x;insta[x]=true; 27 for(int i=head[x],y;i;i=oe[i].nxt){ 28 y=oe[i].to; 29 if(!dfn[y]){ 30 Tarjan(y); 31 low[x]=min(low[x],low[y]); 32 } 33 else if(insta[y]) 34 low[x]=min(low[x],low[y]); 35 } 36 if(low[x]==dfn[x]){ 37 bcc[x]=++col;insta[x]=false; 38 tot[col]++; 39 while(st[top]!=x){ 40 ++tot[col]; 41 insta[st[top]]=false; 42 bcc[st[top--]]=col; 43 } 44 --top; 45 } 46 } 47 48 inline void SPFA1(int S){ 49 memset(vis,0,sizeof(vis)); 50 dis1[S]=tot[S]; 51 queue<int> q; 52 q.push(S); 53 while(!q.empty()){ 54 int x=q.front();q.pop(); 55 vis[x]=false; 56 for(int i=head1[x],y;i;i=e1[i].nxt){ 57 y=e1[i].to; 58 if(dis1[y]<dis1[x]+tot[y]){ 59 dis1[y]=dis1[x]+tot[y]; 60 if(!vis[y]){ 61 q.push(y); 62 vis[y]=true; 63 } 64 } 65 } 66 } 67 } 68 inline void SPFA2(int S){ 69 memset(vis,0,sizeof(vis)); 70 dis2[S]=tot[S]; 71 queue<int> q; 72 q.push(S); 73 while(!q.empty()){ 74 int x=q.front();q.pop(); 75 vis[x]=false; 76 for(int i=head2[x],y;i;i=e2[i].nxt){ 77 y=e2[i].to; 78 if(dis2[y]<dis2[x]+tot[y]){ 79 dis2[y]=dis2[x]+tot[y]; 80 if(!vis[y]){ 81 q.push(y); 82 vis[y]=true; 83 } 84 } 85 } 86 } 87 } 88 int main(){ 89 scanf("%d%d",&n,&m); 90 for(int i=1,u,v;i<=m;++i){ 91 scanf("%d%d",&u,&v); 92 add_edge(u,v); 93 } 94 for(int i=1;i<=n;++i) 95 if(!dfn[i]) 96 Tarjan(i); 97 for(int x=1;x<=n;++x) 98 for(int i=head[x],y;i;i=oe[i].nxt){ 99 y=oe[i].to; 100 if(bcc[x]!=bcc[y]){ 101 add_edge1(bcc[x],bcc[y]); 102 add_edge2(bcc[y],bcc[x]); 103 } 104 } 105 SPFA1(bcc[1]); 106 SPFA2(bcc[1]); 107 int ans=tot[bcc[1]]; 108 memset(vis,0,sizeof(vis)); 109 for(int i=1;i<=n;++i){ 110 if(!vis[bcc[i]]&&dis1[bcc[i]]){ 111 int now=bcc[i]; 112 vis[now]=1; 113 for(int j=head2[now],y;j;j=e2[j].nxt){ 114 y=e2[j].to; 115 if(!dis2[y]) continue; 116 ans=max(ans,dis1[now]+dis2[y]-tot[bcc[1]]); 117 } 118 } 119 } 120 printf("%d ",ans); 121 return 0; 122 }