2 条题解
-
0
upd 2026/6/24:hack 了 yzc 的代码。
请注意有向图缩完后其实是个 DAG,你根本回不去。
所以逆向走其实必须从一个 scc[1] 能到的连通块回到一个能到 scc[1] 的连通块。
正向 bfs 一遍反向 bfs 一遍确认能到 scc[1] 和 scc[1] 能到的连通块。
再根据这两个图分别正向拓扑和反向拓扑求权值。 然后遍历每条边看看是不是逆向能回到 1 就行了。
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; vector<int>G[N],G2[N],G3[N]; int tsp,cnt,scc[N],low[N],dfn[N],rd[N],rd1[N],siz[N]; stack<int>stk;bool instk[N]; void tarjan(int x) { low[x]=dfn[x]=++tsp; stk.push(x);instk[x]=1; for(int y:G[x]) { if(dfn[y]==0) { tarjan(y); low[x]=min(low[x],low[y]); } else if(instk[y])low[x]=min(low[x],dfn[y]); } if(low[x]==dfn[x]) { cnt++; for(int z=-1;z!=x;) { z=stk.top();stk.pop();instk[z]=false; scc[z]=cnt;siz[cnt]++; } } } int dp[N],dp1[N],v[N],v1[N]; int main() { int n,m;cin>>n>>m; for(int i=1;i<=m;i++) { int x,y;cin>>x>>y; G[x].push_back(y); } for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i); map<pair<int,int>,int>mp; for(int i=1;i<=n;i++)for(auto j:G[i]) { int x=scc[i],y=scc[j]; if(!mp[{x,y}]&&x!=y) G2[x].push_back(y),G3[y].push_back(x),mp[{x,y}]=1; } deque<int>q; q.push_back(scc[1]);v[scc[1]]=1; while(!q.empty()) { int x=q.front();q.pop_front(); for(int y:G2[x])if(!v[y]) v[y]=1,q.push_back(y); } q.push_back(scc[1]);v1[scc[1]]=1; while(!q.empty()) { int x=q.front();q.pop_front(); for(int y:G3[x])if(!v1[y]) v1[y]=1,q.push_back(y); } for(int i=1;i<=cnt;i++) { for(int j:G2[i]) { if(v1[i]&&v1[j]) rd1[i]++; if(v[i]&&v[j]) rd[j]++; } } q.push_back(scc[1]); while(!q.empty()) { int x=q.front();q.pop_front(); dp[x]+=siz[x]; for(int y:G2[x]) { dp[y]=max(dp[y],dp[x]); rd[y]--;if(rd[y]==0)q.push_back(y); } } q.push_back(scc[1]); while(!q.empty()) { int x=q.front();q.pop_front(); dp1[x]+=siz[x]; for(int y:G3[x]) { dp1[y]=max(dp1[y],dp1[x]); rd1[y]--;if(rd1[y]==0)q.push_back(y); } } int ans=dp[scc[1]]; for(int i=1;i<=cnt;i++)if(v1[i]) for(int j:G2[i])if(v[j]) ans=max(ans,dp1[i]+dp[j]-siz[scc[1]]); cout<<ans; return 0; } -
0

// SCC 缩点 Tarjan 算法 O(N) #include<bits/stdc++.h> using namespace std; const int N=100005; vector<int> e[N],e1[N],e2[N]; int n,m,ans; int dfn[N],low[N],stk[N],top,scc[N],siz[N],cnt; int d1[N],d2[N]; void tarjan(int x){ //SCC缩点 dfn[x]=low[x]=++dfn[0]; stk[++top]=x; for(auto y:e[x]){ if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]); else if(!scc[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ ++cnt; while(stk[top+1]!=x) scc[stk[top--]]=cnt,siz[cnt]++; } } int main(){ scanf("%d%d",&n,&m); for(int a,b;m--;) scanf("%d%d",&a,&b),e[a].push_back(b); for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i); //SCC缩点 for(int x=1; x<=n; x++)for(auto y:e[x]){ //枚举原始点的邻接点 int a=scc[x],b=scc[y]; if(a!=b) e1[a].push_back(b); //对缩点建正图 if(a!=b) e2[b].push_back(a); //对缩点建反图 } int s=scc[1]; //1号点做起点 d1[s]=siz[s]; //起点自身可达 for(int x=cnt; x; x--)for(auto y:e1[x]) //枚举缩点的邻接点 if(d1[x]) d1[y]=max(d1[y],d1[x]+siz[y]); //正图从s到各点的最长路d1[] d2[s]=siz[s]; //起点自身可达 for(int x=1; x<=cnt; x++)for(auto y:e2[x]) //枚举缩点的邻接点 if(d2[x]) d2[y]=max(d2[y],d2[x]+siz[y]); //反图从s到各点的最长路d2[] ans=siz[s]; //从s出不去的情况 for(int x=1; x<=n; x++)for(auto y:e[x]){ //枚举原始点的邻接点 if(d1[scc[y]] && d2[scc[x]]) //正图能到y且反图能到x ans=max(ans,d1[scc[y]]+d2[scc[x]]-siz[s]); //拼接时s点算了两次 } printf("%d\n",ans); }
- 1
信息
- ID
- 6731
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- 递交数
- 28
- 已通过
- 5
- 上传者