「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 }

 

 

 

posted @ 2018-09-04 21:01  Iamhx  阅读(187)  评论(0编辑  收藏  举报