题意分析:发现,最大半连通子图与强连通分量类似,也是一种强连通分量,求最大数量,就是求出本图中缩点后的一个单链,找出其中权值最大的,可以运用拓扑dp来实现,设f[v]是以v为终点的最大权值,ans为点数(题目答案),d【v】为点v权值;
状转:f[v]=f[u]+d[v],ans[v]=ans[u](f[v]<f[u]+d[v);
ans[v]+=ans[u](f[v]==f[u]+d[v])
强连通分量的思考题:
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int que[N], dfn[N], low[N], stak[N], col[N], num[N], mod, ru[N], f[N], ans[N], f1, ans1 = 0;
int qr, tot, time1 = 0, n, m, top = 0;
vector<int> e1[N], e2[N];
inline void tarjan(int x) {
dfn[x] = low[x] = ++time1;
stak[++top] = x;
for (int v : e1[x]) {
if (!dfn[v]) {
tarjan(v);
low[x] = min(low[x], low[v]);
} else if (!col[v]) {
low[x] = min(low[x], dfn[v]);
}
}
if (dfn[x] == low[x]) {
col[x] = ++tot;
num[tot] = 1;
int y;
while (y = stak[top--], y != x) {
col[y] = tot, num[tot]++;
}
}
return;
}
int main() {
cin >> n >> m >> mod;
for (int i = 1, x, y; i <= m; i++) {
cin >> x >> y;
e1[x].push_back(y);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
tarjan(i);//缩点
}
}
for (int x = 1; x <= n; x++) {
for (int y : e1[x]) {
if (col[x] != col[y]) {
e2[col[x]].push_back(col[y]);//新建一个有向无环图
}
}
}
for (int x = 1; x <= tot; x++) {
sort(e2[x].begin(), e2[x].end());
e2[x].erase(unique(e2[x].begin(), e2[x].end()), e2[x].end());
for (int y : e2[x]) {
++ru[y];
}
}
for (int x = 1; x <= tot; x++) {
if (!ru[x]) {
que[++qr] = x, f[x] = num[x], ans[x] = 1;
}
}
for (int i = 1, x; i <= qr; i++) {
x = que[i];
for (int y : e2[x]) {
if (f[y] < f[x] + num[y]) {
f[y] = f[x] + num[y];
ans[y] = ans[x];
} else if (f[y] == f[x] + num[y]) {
(ans[y] += ans[x]) %= mod;//拓扑dp
}
if (!--ru[y])
que[++qr] = y;
}
}
for (int x = 1; x <= tot; x++) {
if (f[x] > f1) {
f1 = f[x], ans1 = ans[x];
} else if (f[x] == f1) {
(ans1 += ans[x]) %= mod;//合并答案
}
}
cout << f1 << endl << ans1 << endl;
return 0;
}