「BZOJ1093」[ZJOI2007] 最大半连通子图
题意:
给你一张图,要你新建一张子图。要求枚举原图中的所有边,如果某一条边链接的两个节点都在子图中,这条边一定要在子图中。如果新建的子图中的任意两点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 }