這題簡單來說要問的是,給定一張有向圖,那麼把其中一條邊反向之後可以得到的 1 所在的SCC 大小最大為多少。首先對這張圖進行 SCC 縮點,變成了一張每個點都有點權的 DAG 。如果我們把 u -> v 的邊反向,那麼這時候的路徑就會是:從 1 所在的 SCC 開始,沿著 DAG 的邊走到 v 所在的 SCC ,然後跳到 u 所在的SCC,再走回 1 所在的 SCC 。於是我們對縮完點後的 DAG 的每個點先 dp 算出「從這個點走到 1 所在的 SCC ,經過的點權總和最大為多少」,還有「從 1 所在的 SCC 走到這個點,經過的點權總和最大為多少」,就可以知道任何一條邊被反向之後所得到的 SCC 大小為多少了。
code :
#include<bits/stdc++.h> #define INF 100000000 using namespace std; const int maxn=100000+10 ; vector<int> v1[maxn],v2[maxn],v[maxn] ; bool vis[maxn] ; int topo[maxn],t ; int scnt,scc[maxn],size[maxn] ; void dfs1(int x) { vis[x]=1 ; for(auto i : v1[x]) if(!vis[i]) dfs1(i) ; topo[--t]=x ; } void dfs2(int x) { vis[x]=1 ; scc[x]=scnt ; size[scnt]++ ; for(auto i : v2[x]) if(!vis[i]) dfs2(i) ; } int d1[maxn],d2[maxn] ; int dp(int x,int *d) { if(d[x]!=-INF) return d[x] ; if(x==scc[1]) return d[x]=size[scc[1]] ; for(auto i : v[x]) d[x]=max(d[x],size[x]+dp(i,d)) ; return d[x] ; } main() { if(fopen("grass.in","r")) { freopen("grass.in","r",stdin) ; freopen("grass.out","w",stdout) ; } int n,m ; scanf("%d%d",&n,&m) ; while(m--) { int x,y ; scanf("%d%d",&x,&y) ; v1[x].push_back(y) ; v2[y].push_back(x) ; } t=n+1 ; for(int i=1;i<=n;i++) if(!vis[i]) dfs1(i) ; memset(vis,0,sizeof(vis)) ; scnt=0 ; for(int i=1;i<=n;i++) if(!vis[topo[i]]) scnt++ , dfs2(topo[i]) ; for(int i=1;i<=n;i++) for(auto j : v1[i]) if(scc[i]!=scc[j]) v[scc[i]].push_back(scc[j]) ; fill(d1+1,d1+n+1,-INF) ; for(int i=1;i<=scnt;i++) dp(i,d1) ; for(int i=1;i<=scnt;i++) v[i].clear() ; for(int i=1;i<=n;i++) for(auto j : v1[i]) if(scc[i]!=scc[j]) v[scc[j]].push_back(scc[i]) ; fill(d2+1,d2+n+1,-INF) ; for(int i=1;i<=scnt;i++) dp(i,d2) ; int ans=0 ; for(int i=1;i<=n;i++) for(auto j : v1[i]) ans=max(ans,d1[scc[i]]+d2[scc[j]]-size[scc[1]]) ; printf("%d\n",ans) ; }
沒有留言:
張貼留言