[ZJOI2007]最大半连通子图
Description
Solution
强连通一定半联通,所以就缩一下点。然后DAG上DP一下最长链就可以了,注意边表去一下重防止计数出错。
Code
一开始不会去重,所以蒯了别人的去重方法,代码可能有点丑。
const int N = 100010;
const int M = 1000010;
struct Graph {
int hd[N], to[M], nxt[M], cnt;
void adde(int x, int y) {
to[++cnt] = y;
nxt[cnt] = hd[x];
hd[x] = cnt;
}
} G;
int n, m, ha, f[N], g[N];
int w[N], dfn[N], low[N], dft, sta[N], top, ins[N], bel[N], tot, deg[N], ind[N];
std::vector<int> DAG[N];
std::queue<int> Q;
void tarjan(int x) {
ins[sta[++top] = x] = 1;
dfn[x] = low[x] = ++dft;
for (int i = G.hd[x]; i; i = G.nxt[i]) {
int &v = G.to[i];
if (!dfn[v]) {
tarjan(v);
low[x] = std::min(low[x], low[v]);
} else if (ins[v])
low[x] = std::min(low[x], dfn[v]);
}
if (dfn[x] == low[x]) {
++tot;
int y;
do {
y = sta[top--];
bel[y] = tot;
ins[y] = 0;
w[tot]++;
} while (y != x);
}
}
void main() {
n = read(), m = read(), ha = read();
for (int i = 1, x, y; i <= m; ++i) {
x = read(), y = read();
G.adde(x, y);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; ++i) {
for (int j = G.hd[i]; j; j = G.nxt[j])
if (bel[G.to[j]] != bel[i]) {
DAG[bel[i]].push_back(bel[G.to[j]]);
}
}
for (int i = 1; i <= tot; ++i) {
std::sort(DAG[i].begin(), DAG[i].end());
deg[i] = std::unique(DAG[i].begin(), DAG[i].end()) - DAG[i].begin();
for (int j = 0; j < deg[i]; ++j) ind[DAG[i][j]]++;
f[i] = w[i];
g[i] = 1;
}
for (int i = 1; i <= tot; ++i)
if (!ind[i]) Q.push(i);
while (!Q.empty()) {
int x = Q.front();
Q.pop();
for (int i = 0; i < deg[x]; ++i) {
int &y = DAG[x][i];
if (f[x] + w[y] > f[y])
f[y] = f[x] + w[y], g[y] = g[x];
else if (f[x] + w[y] == f[y])
g[y] = (g[y] + g[x]) % ha;
if (--ind[y] == 0) Q.push(y);
}
}
int ans1 = 0, ans2 = 0;
for (int i = 1; i <= tot; ++i)
if (f[i] > ans1) {
ans1 = f[i];
ans2 = g[i];
} else if (f[i] == ans1) {
ans2 = (ans2 + g[i]) % ha;
}
printf("%d %d\n", ans1, ans2);
}