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

 

转载于:https://www.cnblogs.com/yyf0309/p/7326267.html

你可能感兴趣的:(bzoj 1093 最大半连通子图 - Tarjan - 拓扑排序 - 动态规划)