P2272 [ZJOI2007]最大半連通子圖
阿新 • • 發佈:2021-07-29
Problem
當一個有向圖\(G= (V,E)\)滿足:\(\forall u,v \in V,u \to v\)或\(v \to u\)。
求這個圖的最大半聯通子圖(節點數最多)節點數和最大半聯通子圖個數模\(X\)的值。
\(n \le 10^5,m \le 10^6,X \le 10^8\)
Solution
顯然SCC也是強連通子圖。所以先用Tarjan縮點,然後將圖變成一個DAG(套路)。問題轉變成:求縮點完後的DAG中的最長鏈和最長鏈個數。顯然可以拓撲+dp。
注意:縮點後會出現重邊,要注意不能讓重邊加入,用個map記錄即可。
# include <bits/stdc++.h> using namespace std; # define int long long const int N = 1e5 + 5; int n,m,X; vector <int> g[N],g2[N]; vector <pair<int,int> > G; int dfn[N],low[N],dfntot = 0,s[N],top = 0,scc_tot = 0,siz[N],belong[N],du[N]; bool vis[N]; int dis[N],cnt[N]; map<int,map<int,int> > check; void dfs(int x) { dfn[x] = low[x] = ++dfntot; s[++top] = x; vis[x] = 1; for(int i = 0; i < (int)g[x].size(); i++) { int v = g[x][i]; if(!dfn[v]) { dfs(v); low[x] = min(low[x],low[v]); } else if(vis[v]) low[x] = min(low[x],dfn[v]); } if(dfn[x] == low[x]) { int temp = 0; ++scc_tot; do { temp = s[top--]; ++siz[scc_tot]; belong[temp] = scc_tot; vis[temp] = 0; }while(x != temp); } return; } void topsort(void) { queue <int> q; for(int i = 1; i <= scc_tot; i++) { if(du[i] == 0) {q.push(i); dis[i] = siz[i],cnt[i] = 1;} } while(!q.empty()) { int x = q.front(); q.pop(); for(int i = 0; i < (int)g2[x].size(); i++) { int v = g2[x][i]; if(siz[v] + dis[x] > dis[v]) { dis[v] = dis[x] + siz[v]; cnt[v] = cnt[x]; } else if(siz[v] + dis[x] == dis[v]) { cnt[v] = (cnt[v] + cnt[x]) % X; } if(--du[v] == 0) q.push(v); } } return; } signed main(void) { scanf("%lld%lld%lld",&n,&m,&X); for(int i = 1; i <= m; i++) { int a,b; scanf("%lld%lld",&a,&b); g[a].push_back(b); G.push_back(make_pair(a,b)); } for(int i = 1; i <= n; i++) { if(!dfn[i]) dfs(i); } for(int i = 0; i < m; i++) { int u = belong[G[i].first],v = belong[G[i].second]; if(u != v && !check[u][v]) g2[u].push_back(v),du[v]++,check[u][v] = 1; } topsort(); int ans1 = 0,ans2 = 0; for(int i = 1; i <= scc_tot; i++) { if(ans1 < dis[i]) {ans1 = dis[i]; ans2 = cnt[i] % X;} else if(ans1 == dis[i]) {ans2 = (ans2 + cnt[i]) % X;} } printf("%lld\n%lld",ans1 % X,ans2); return 0; }