1514:【例 2】最大半連通子圖
阿新 • • 發佈:2021-07-05
【題目描述】
原題來自:ZJOI 2007
一個有向圖 G=(V,E)
稱為半連通的 (Semi-Connected),如果滿足:∀u,v∈V,滿足 u→v或 v→u,即對於圖中任意兩點 u,v,存在一條 u 到 v 的有向路徑或者從 v 到 u
的有向路徑。
若 G'=(V',E')
滿足,E' 是 E 中所有和V' 有關的邊,則稱G' 是 G 的一個匯出子圖。若 G' 是 G 的匯出子圖,且 G' 半連通,則稱 G' 為 G 的半連通子圖。若G' 是 G 所有半連通子圖中包含節點數最多的,則稱 G' 是 G
的最大半連通子圖。
給定一個有向圖 G
,請求出 G 的最大半連通子圖擁有的節點數 K,以及不同的最大半連通子圖的數目 C。由於 C 可能比較大,僅要求輸出 C 對 X
的餘數。
【輸入】
第一行包含三個整數 N,M,X
。N,M 分別表示圖 G 的點數與邊數,X
的意義如上文所述;
接下來 M
行,每行兩個正整數 a,b,表示一條有向邊 (a,b
)。
圖中的每個點將編號為 1,2,3,⋯,N
,保證輸入中同一個 (a,b
)不會出現兩次。
【輸出】
應包含兩行。第一行包含一個整數 K
,第二行包含整數CmodX
。
【輸入樣例】
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
【輸出樣例】
3
3
【提示】
對於 20% 的資料,N≤18
;
對於 60% 的資料,N≤104
;
對於 100% 的資料,1≤N≤105,1≤M≤106,X≤108
。
#include <cstdio> #include <cstring> #include <algorithm> #include <set> #include <map> #pragma GCC optimize (2) using namespace std; #define ll long long const int N = 100010, M = 2000010; int dfn[N], low[N], timestamp; int n, m, x; int stk[N], in_stk[N], top; int id[N], scc_cnt, scc_size[N]; int f[N], g[N]; int h[N], hs[N], w[M], ne[M], e[M], idx; void add(int h[], int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } void tarjan(int u) { low[u] = dfn[u] = ++timestamp; stk[++top] = u; in_stk[u] = 1; for (int i = h[u] ; ~i ; i = ne[i]) { int j = e[i]; if (!dfn[j]) { tarjan(j); low[u] = min(low[u], low[j]); } else if (in_stk[j]) { low[u] = min(low[u], dfn[j]); } } if (low[u] == dfn[u]) { ++scc_cnt; int y; do { y = stk[top--]; in_stk[y] = 0; id[y] = scc_cnt; scc_size[scc_cnt]++; } while (y != u); } } int main() { memset(h, -1, sizeof(h)); memset(hs, -1, sizeof(hs)); scanf("%d %d %d", &n, &m, &x); for (int i = 1; i <= m; i++) { int a, b; scanf("%d %d", &a, &b); add(h, a, b); } for (int i = 1; i <= n; i++) { if (!dfn[i]) tarjan(i); } map <ll, int> m; m.clear(); for (int i = 1; i <= n; i++) { for (int j = h[i]; ~j; j = ne[j]) { int k = e[j]; int a = id[i], b = id[k]; ll hash = a * 1000000ll + b; if (a != b && m[hash] == 0) { add(hs, a, b); m[hash] = 1; } } } for (int i = scc_cnt; i ; i--) { if (!f[i]) { f[i] = scc_size[i]; g[i] = 1; } for (int j = hs[i]; ~j ; j = ne[j]) { int k = e[j]; if (f[k] < f[i] + scc_size[k]) { f[k] = f[i] + scc_size[k]; g[k] = g[i]; } else if (f[k] == f[i] + scc_size[k]) { g[k] = (g[k] + g[i]) % x; } } } int maxx = 0, sum = 0; for (int i = 1; i <= scc_cnt; i++) { if (f[i] > maxx) { maxx = f[i]; sum = g[i]; } else if (f[i] == maxx) { sum = (sum + g[i]) % x; } } printf("%d\n%d", maxx, sum); return 0; }