1. 程式人生 > >「BZOJ1093」[ZJOI2007] 最大半連通子圖

「BZOJ1093」[ZJOI2007] 最大半連通子圖

XML 拓撲dp can printf build min ans clu const

題意:

  給你一張圖,要你新建一張子圖。要求枚舉原圖中的所有邊,如果某一條邊鏈接的兩個節點都在子圖中,這條邊一定要在子圖中。如果新建的子圖中的任意兩點u, v滿足u可以到v或v可以到u,則稱這個子圖為“半連通子圖”。要你求出最大的半連通子圖的節點數,以及最大的半連通子圖的方案數有多少(方案數對C取模)

題解:

  顯然必須先縮點,同一個強連通分量裏面的點一定滿足半聯通子圖的定義。在縮點後建好的新圖上面跑一個拓撲DP計算最長鏈和方案數。

  

 1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 const
int maxn = 100005; 6 const int maxm = 1000005; 7 8 int n, m, MOD; 9 int head[maxn], e[maxm << 1], toit[maxm << 1], nxt[maxm << 1]; 10 int dfn[maxn], low[maxn], q[maxn << 1], bel[maxn], hav[maxn]; 11 vector<int> G[maxn]; 12 bool inq[maxn]; 13 14 int tot = 1; 15 void Add (int
u,int v) { 16 toit[++ tot] = v; nxt[tot] = head[u]; head[u] = tot; 17 } 18 19 int t = 0, scc = 0, top = 0; 20 void Tarjan (int x) { 21 dfn[x] = low[x] = ++ t; q[++ top] = x; inq[x] = 1; 22 for (int i = head[x]; i; i = nxt[i]) { 23 int v = toit[i]; 24 if (! dfn[v]) { 25 Tarjan(v); low[x] = min(low[x], low[v]);
26 } else if (inq[v]) low[x] = min(low[x], dfn[v]); 27 } 28 int now = 0; 29 if (dfn[x] == low[x]) { 30 ++ scc; 31 while (now != x) { 32 now = q[top --]; inq[now] = 0; hav[scc] ++; bel[now] = scc; 33 } 34 } 35 } 36 37 int ind[maxn]; 38 void Rebuild () { 39 for (int x = 1; x <= n; ++ x) { 40 for (int i = head[x]; i; i = nxt[i]) { 41 int v = toit[i]; 42 if (bel[x] != bel[v]) { 43 G[bel[x]].push_back(bel[v]); 44 ++ ind[bel[v]]; 45 } 46 } 47 } 48 } 49 50 int f[maxn], g[maxn], vis[maxn]; 51 void dp () { 52 queue<int> Q; 53 for (int i = 1; i <= scc; ++ i) { 54 if (! ind[i]) Q.push(i); 55 f[i] = hav[i]; g[i] = 1; 56 } 57 while (! Q.empty()) { 58 int x = Q.front(); Q.pop(); 59 for (unsigned i = 0; i < G[x].size(); ++ i) { 60 int v = G[x][i]; -- ind[v]; 61 if (! ind[v]) Q.push(v); 62 if (vis[v] == x) continue; 63 if (f[x] + hav[v] > f[v]) { 64 f[v] = f[x] + hav[v]; 65 g[v] = g[x]; 66 } else if (f[x] + hav[v] == f[v]) { 67 g[v] = (g[v] + g[x]) % MOD; 68 } 69 vis[v] = x; 70 } 71 } 72 } 73 74 int main () { 75 scanf("%d%d%d", &n, &m, &MOD); 76 for (int i = 1; i <= m; ++ i) { 77 int u, v; scanf("%d%d", &u, &v); 78 Add(u, v); 79 } 80 for (int i = 1; i <= n; ++ i) if (! dfn[i]) Tarjan(i); 81 Rebuild(); 82 dp(); 83 int Mx = 0, Ans = 0; 84 for (int i = 1; i <= scc; ++ i) { 85 if (f[i] > Mx) Mx = f[i], Ans = g[i]; 86 else if (f[i] == Mx) Ans = (Ans + g[i]) % MOD; 87 } 88 printf("%d\n%d\n", Mx, Ans); 89 return 0; 90 }

「BZOJ1093」[ZJOI2007] 最大半連通子圖