bzoj 1093 最大半连通子图 - Tarjan - 拓扑排序 - 动态规划
一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。若G'=(V',E')满足V'?V,E'是E中所有跟V'有关的边,则称G'是G的一个导出子图。若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图。若G'是G所有半连通子图中包含节点数最多的,则称G'是G的最大半连通子图。给定一个有向图G,请求出G的最大半连通子图拥有的节点数K,以及不同的最大半连通子图的数目C。由于C可能比较大,仅要求输出C对X的余数。
Input
第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。N ≤100000, M ≤1000000;对于100%的数据, X ≤10^8
Output
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
Sample Input
6 6 20070603 1 2 2 1 1 3 2 4 5 6 6 4
Sample Output
3 3
题目大意 一个有向图G(V, U)是半连通的,当且仅当任意点,并且存在一条路径,它上面的所有边属于U,并且从u到v或者从v到u。图G的导出子图G‘(V'. U'),满足.一个有向图G的半连通子图是一个导出子图且半连通,最大半连通子图是其中拥有最多点数的半连通子图。问最大的半连通子图的点数和数量。
因为半连通子图一定是导出子图,所以两个半连通子图是否同构之和它们的定点集合有关。
由于图上可能有环(强连通分量),所以考虑缩点。缩点后的图是个DAG,然后你可得到一个结论就是这个DAG上的一条路径就是原图的一个半连通子图,并且DAG上的路径和原图中的半连通子图一一对应。
我们可以赋予每个点一个点权,代表它在原图中代表的点数。
于是这个问题转换成在DAG上最长路及其计数。这个拓扑排序再加个小dp就可以水过了。
另外注意拓扑排序的时判断重边。
Code
1 /** 2 * bzoj 3 * Problem#1093 4 * Accepted 5 * Time: 1988ms 6 * Memory: 13740k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 #define smin(a, b) a = min(a, b) 12 #define smax(a, b) b = max(a, b) 13 14 int n, m; 15 int moder; 16 vector<int> *g; 17 18 inline void init() { 19 scanf("%d%d%d", &n, &m, &moder); 20 g = new vector<int>[(n + 1)]; 21 for(int i = 1, u, v; i <= m; i++) { 22 scanf("%d%d", &u, &v); 23 g[u].push_back(v); 24 } 25 } 26 27 int cnt = 0; 28 stack<int> s; 29 int* visitID; 30 int* exitID; 31 int* belong; 32 boolean *visited; 33 boolean *instack; 34 inline void init_tarjan() { 35 visitID = new int[(n + 1)]; 36 exitID = new int[(n + 1)]; 37 visited = new boolean[(n + 1)]; 38 instack = new boolean[(n + 1)]; 39 belong = new int[(n + 1)]; 40 memset(visited, false, sizeof(boolean) * (n + 1)); 41 memset(instack, false, sizeof(boolean) * (n + 1)); 42 } 43 44 void tarjan(int node) { 45 visitID[node] = exitID[node] = ++cnt; 46 visited[node] = instack[node] = true; 47 s.push(node); 48 49 for(int i = 0; i < (signed)g[node].size(); i++) { 50 int& e = g[node][i]; 51 if(!visited[e]) { 52 tarjan(e); 53 smin(exitID[node], exitID[e]); 54 } else if(instack[e]) { 55 smin(exitID[node], visitID[e]); 56 } 57 } 58 59 if(visitID[node] == exitID[node]) { 60 int e; 61 do { 62 e = s.top(); 63 s.pop(); 64 instack[e] = false; 65 belong[e] = node; 66 } while(e != node); 67 } 68 } 69 70 vector<int> *ng; 71 int* dag; 72 int *val; 73 inline void rebuild() { 74 dag = new int[(n + 1)]; 75 ng = new vector<int>[(n + 1)]; 76 val = new int[(n + 1)]; 77 memset(val, 0, sizeof(int) * (n + 1)); 78 memset(dag, 0, sizeof(int) * (n + 1)); 79 80 for(int i = 1; i <= n; i++) 81 for(int j = 0; j < (signed)g[i].size(); j++) { 82 int& e = g[i][j]; 83 if(belong[e] != belong[i]) 84 ng[belong[i]].push_back(belong[e]), dag[belong[e]]++; 85 } 86 87 for(int i = 1; i <= n; i++) 88 val[belong[i]]++; 89 } 90 91 queue<int> que; 92 int *dis; 93 int *counter; 94 inline void topu() { 95 dis = new int[(n + 1)]; 96 counter = new int[(n + 1)]; 97 memset(dis, 0, sizeof(int) * (n + 1)); 98 memset(visited, false, sizeof(boolean) * (n + 1)); 99 100 for(int i = 1; i <= n; i++) 101 if(belong[i] == i && !dag[i]) 102 que.push(i), dis[i] = val[i], counter[i] = 1; 103 104 while(!que.empty()) { 105 int e = que.front(); 106 que.pop(); 107 for(int i = 0; i < (signed)ng[e].size(); i++) { 108 int& eu = ng[e][i]; 109 dag[eu]--; 110 if(!dag[eu]) 111 que.push(eu); 112 if(visited[eu]) continue; 113 visited[eu] = true; 114 115 if(dis[e] + val[eu] > dis[eu]) { 116 dis[eu] = dis[e] + val[eu]; 117 counter[eu] = counter[e]; 118 } else if(dis[e] + val[eu] == dis[eu]) 119 counter[eu] = (counter[eu] + counter[e]) % moder; 120 } 121 for(int i = 0; i < (signed)ng[e].size(); i++) 122 visited[ng[e][i]] = false; 123 } 124 } 125 126 int maxdis = -1, res = 0; 127 inline void solve() { 128 for(int i = 1; i <= n; i++) { 129 if(belong[i] != i) continue; 130 if(dis[i] > maxdis) { 131 maxdis = dis[i]; 132 res = counter[i]; 133 } else if(dis[i] == maxdis) 134 (res += counter[i]) %= moder; 135 } 136 printf("%d\n%d", maxdis, res); 137 } 138 139 int main() { 140 init(); 141 init_tarjan(); 142 for(int i = 1; i <= n; i++) 143 if(!visited[i]) 144 tarjan(i); 145 rebuild(); 146 topu(); 147 solve(); 148 return 0; 149 }