[bzoj1093][ZJOI2007]最大半连通子图
题目大意:一个有向图称为半连通的,当且仅当对于任意两点$u,v$,都满足$u$能到达$v$或者$v$能到达$u$。给一张有向图,问该图最大半连通子图的节点个数及方案数。
题解:发现任意一个强连通分量显然都是半连通子图。把它们都缩成一个点。形成一个$DAG$,问题就转化为了找最长链长度及方案数。$DP$即可
卡点:方案数忘取模
C++ Code:
#include <cstdio> #include <algorithm> #define maxn 100010 #define maxm 1000010 #define ONLINEJUDGE #define read() R::READ() #include <cctype> namespace R { int x; #ifdef ONLINE_JUDGE char *ch, op[1 << 26]; inline void init() { fread(ch = op, 1, 1 << 26, stdin); } inline int READ() { while (isspace(*ch)) ch++; for (x = *ch & 15, ch++; isdigit(*ch); ch++) x = x * 10 + (*ch & 15); return x; } #else char ch; inline int READ() { ch = getchar(); while (isspace(ch)) ch = getchar(); for (x = ch & 15, ch = getchar(); isdigit(ch); ch = getchar()) x = x * 10 + (ch & 15); return x; } #endif } int n, m, mod, ans, num; inline void getans(int a, int b) { if (ans == a) (num += b) %= mod; if (ans < a) ans = a, num = b; } inline int min(int a, int b) {return a < b ? a : b;} namespace Tree { int sz[maxn], ind[maxn], CNT; int head[maxn], cnt; struct Edge { int to, nxt; } e[maxm]; void add(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; ind[b]++; } int q[maxn], h = 1, t = 0; int f[maxn], num[maxn]; void bfs() { for (int i = 1; i <= CNT; i++) if (!ind[i]) { q[++t] = i; f[i] = sz[i]; num[i]++; getans(f[i], num[i]); } while (h <= t) { int u = q[h++]; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (f[v] < f[u] + sz[v]) { f[v] = f[u] + sz[v]; num[v] = num[u]; } else if (f[v] == f[u] + sz[v]) (num[v] += num[u]) %= mod; if (!--ind[v]) { getans(f[v], num[v]); q[++t] = v; } } } } } namespace Graph { int res[maxn]; int head[maxn], cnt; struct Edge { int from, to, nxt; inline bool operator < (const Edge &rhs) const{ if (res[from] == res[rhs.from]) return res[to] < res[rhs.to]; else return res[from] < res[rhs.from]; } } e[maxm]; void add(int a, int b) { e[++cnt] = (Edge) {a, b, head[a]}; head[a] = cnt; } int DFN[maxn], low[maxn], idx; int S[maxn], top; bool ins[maxn]; void Tarjan(int u) { DFN[u] = low[u] = ++idx; ins[S[++top] = u] = true; int v; for (int i = head[u]; i; i = e[i].nxt) { v = e[i].to; if (!DFN[v]) { Tarjan(v); low[u] = min(low[u], low[v]); } else if (ins[v]) low[u] = min(low[u], DFN[v]); } if (low[u] == DFN[u]) { Tree::CNT++; do { ins[v = S[top--]] = false; Tree::sz[res[v] = Tree::CNT]++; } while (v != u); } } inline void tarjan(int n) { for (int i = 1; i <= n; i++) if (!DFN[i]) Tarjan(i); } void init() { std::sort(e + 1, e + cnt + 1); for (int i = 1; i <= cnt; i++) { if (res[e[i].from] == res[e[i - 1].from] && res[e[i].to] == res[e[i - 1].to]) continue; if (res[e[i].from] != res[e[i].to]) Tree::add(res[e[i].from], res[e[i].to]); } } } int main() { #ifdef ONLINE_JUDGE R::init(); #endif n = read(), m = read(), mod = read(); for (int i = 1; i <= m; i++) { int a = read(), b = read(); Graph::add(a, b); } Graph::tarjan(n); Graph::init(); Tree::bfs(); printf("%d\n%d\n", ans, num); return 0; }