You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
3.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010, M = 2000010; // 因为要建新图,两倍的边
int n, m, mod; // 点数、边数、取模的数
int f[N], g[N];
int h[N], hs[N], e[M], ne[M], idx; // h: 原图hs: 新图
void add(int h[], int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// tarjan求强连通分量
int dfn[N], low[N], ts, in_stk[N], stk[N], top;
int id[N], scc_cnt, sz[N];
void tarjan(int u) {
dfn[u] = low[u] = ++ts;
stk[++top] = u;
in_stk[u] = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (in_stk[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
++scc_cnt;
int x;
do {
x = stk[top--];
in_stk[x] = 0;
id[x] = scc_cnt;
sz[scc_cnt]++;
} while (x != u);
}
}
int main() {
memset(h, -1, sizeof h); // 原图
memset(hs, -1, sizeof hs); // 新图
scanf("%d %d %d", &n, &m, &mod);
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
add(h, a, b);
}
// (1) tarjan算法求强连通分量
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
// (2) 缩点,建新图
unordered_set<LL> S; // 对连着两个不同强连通分量的边进行判重
for (int u = 1; u <= n; u++)
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
int a = id[u], b = id[v];
LL hash = a * 1000000ll + b; // 新两点a,b之间的Hash值防止出现重边
if (a != b && !S.count(hash)) { // ① 不同的连通块 ② 非重边
add(hs, a, b); // 加入新图
S.insert(hash); // 记录a,b端点的边使用过
}
}
// (3) tarjan统计出来的前连通分量是逆拓扑序的
// 计算以每个新点强连通分量为终点的最长链中的点的数目f[i]和数量g[i]
for (int u = scc_cnt; u; u--) { // 因此dp的初始点就在id的最后一个因此我们逆着id做就是拓扑序了
if (!f[u]) { // u节点被初次访问时
f[u] = sz[u]; // 最长链中点数量=存储u号强连通分量中节点的个数
g[u] = 1; // 最长链的条数=1条
}
// 对DAG开始进行递推层次感好强
for (int i = hs[u]; ~i; i = ne[i]) {
int v = e[i];
if (f[v] < f[u] + sz[v]) {
f[v] = f[u] + sz[v]; // 最大值被打破(联想数字三角形)
g[v] = g[u]; // 同步更新条数
} else if (f[v] == f[u] + sz[v]) // 最大值被追平,则叠加条数
g[v] = (g[v] + g[u]) % mod;
}
}
// (4) 求解答案
int res = 0, sum = 0; // 最大半连通子图节点数、对应方案数
for (int i = 1; i <= scc_cnt; i++)
if (f[i] > res)
res = f[i], sum = g[i];
else if (f[i] == res)
sum = (sum + g[i]) % mod;
// 输出
printf("%d\n%d\n", res, sum);
return 0;
}