题意:
给你一张图,要你新建一张子图。要求枚举原图中的所有边,如果某一条边链接的两个节点都在子图中,这条边一定要在子图中。如果新建的子图中的任意两点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
%d
", Mx, Ans);
89 return 0;
90 }