Files
python/TangDou/AcWing/SCC/1175.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

100 lines
3.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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