cychester

BZOJ 1093 [ZJOI2007]最大半连通子图 - Tarjan 缩点

Description

定义一个半联通图为 : 对任意的两个点$u, v$,都有存在一条路径从$u$到$v$, 从$v$到$u$。

给出一个有向图, 要求出节点最多的半联通子图,  并求出方案数。

 

Solution

先进行一次$Tarjan \ SCC$ 缩点, 得到一个有向无环图, 则半联通子图一定是一条单向的链。

然后就相当于求出最大的链的节点数, 以及有多少种链有这么多节点。

从每个入度为$0$ 的节点开始$DP$即可。 还需要注意同一对联通块的边需要判重。

 

Code

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 #define rd read()
  6 using namespace std;
  7 typedef pair<int, int> P;
  8 
  9 const int N = 1e5 + 5;
 10 const int M = 1e6 + 5;
 11 
 12 int head[N], tot;
 13 int Head[N], Tot;
 14 int low[N], dfn[N], cnt, col, c[N], r[N], sz[N], inq[N], num[N], vis[N], sum[N];
 15 int st[N], tp;
 16 int n, m, mod, ans1, ans2;
 17 
 18 struct edge {
 19     int nxt, to, fr;
 20 }e[M], E[M];
 21 
 22 int read() 
 23 {
 24     int X = 0, p = 1; char c = getchar();
 25     for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1;
 26     for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0';
 27     return X * p;
 28 }
 29 
 30 void add(int u, int v) {
 31     e[++tot].to = v;
 32     e[tot].nxt = head[u];
 33     e[tot].fr = u;
 34     head[u] = tot;
 35 }
 36 
 37 void Add(int u, int v) {
 38     E[++Tot].to = v;
 39     E[Tot].nxt = Head[u];
 40     Head[u] = Tot;
 41     r[v]++;
 42 }
 43 
 44 void tarjan(int u) {
 45     low[u] = dfn[u] = ++cnt;
 46     st[++tp] = u;
 47     inq[u] = 1;
 48     for (int i = head[u]; i; i = e[i].nxt) {
 49         int nt = e[i].to;
 50         if (!dfn[nt]) tarjan(nt), low[u] = min(low[u], low[nt]);
 51         else if (inq[nt]) low[u] = min(low[u], dfn[nt]);
 52     }
 53     if (dfn[u] == low[u]) {
 54         col++;
 55         for (;;) {
 56             int now = st[tp--];
 57             c[now] = col;
 58             inq[now] = 0;
 59             sz[col]++;
 60             if (now == u) 
 61                 break;
 62         }
 63     }
 64 }
 65 
 66 void dp(int u) {
 67     if (num[u]) return;
 68     num[u] = 1;
 69     sum[u] = sz[u];
 70     for (int i = Head[u]; i; i = E[i].nxt) {
 71         int nt = E[i].to;
 72         if (vis[nt] == u) 
 73             continue;
 74         dp(nt); vis[nt] = u;
 75         if (sum[nt] + sz[u] > sum[u])
 76             sum[u] = sum[nt] + sz[u], num[u] = num[nt];
 77         else if (sum[nt] + sz[u] == sum[u])
 78             num[u] = (num[u] + num[nt]) % mod;
 79     }
 80 }
 81 
 82 int main()
 83 {
 84     n = rd; m = rd; mod = rd;
 85     for (int i =  1; i <= m; ++i) {
 86         int u = rd, v = rd;
 87         add(u, v);
 88     }
 89     for (int i = 1; i <= n; ++i)
 90         if (!dfn[i]) tarjan(i);
 91     for (int i = 1; i <= tot; ++i) {
 92         int u = e[i].fr, v = e[i].to;
 93         if (c[u] == c[v])
 94             continue;
 95         Add(c[u], c[v]);
 96     }
 97     for (int i = 1; i <= col; ++i)
 98         if (!r[i]) dp(i);
 99     for (int i = 1; i <= col; ++i)
100         ans1 = max(ans1, sum[i]);
101     for (int i = 1; i <= col; ++i)
102         if (ans1 == sum[i]) ans2 = (ans2 + num[i]) % mod;
103     printf("%d\n%d\n", ans1, ans2);
104 }
View Code

 

posted on 2018-09-18 12:57  cychester  阅读(123)  评论(0编辑  收藏  举报

导航