题目大意:一个有向图称为半连通的,当且仅当对于任意两点$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;
}