题目大意:给出一张有向图G,求一个结点数最大的结点集,使得该点集中任意两个结点u和v满足:
要么u可到达v,要么v可以到达u(u和v互相可达也可以)

解题思路:u和v相互可达的时候,就是两个结点在同一个强连通分量内的时候
首先要保证集合里面的点可达:强连通分量就满足集合内的点都相互可达。所以第一件事就是找出所有的强连通分量,并统计出每个强连通分量内的结点数

然后找出每个强连通分量之间的关系,也就是找出两个强连通分量之间的桥,连接可连接的强连通分量

最后将每个强连通分量收缩,得到SCC图。此时的SCC图就变成了一个DAG,所以题目就转成用DP求DAG了

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
#define N 1010
#define M 50010

struct Edge{
    int to, next;
}E[M];

stack<int> S;
int linklow[N], pre[N], head[N], sccno[N], num[N], dp[N];
int tot, dfs_clock, scc_cnt, n, m;
bool link[N][N];

void dfs(int u) {
    linklow[u] = pre[u] = ++dfs_clock;
    S.push(u);

    for (int i = head[u]; i != -1; i = E[i].next) {
        int v = E[i].to;
        if (!pre[v]) {
            dfs(v);
            linklow[u] = min(linklow[u], linklow[v]);
        }
        else if (!sccno[v]) {
            linklow[u] = min(linklow[u], pre[v]);
        }
    }

    if (pre[u] == linklow[u]) {
        scc_cnt++;
        num[scc_cnt] = 0;
        while (1) {
            int x = S.top();
            S.pop();
            num[scc_cnt]++;
            sccno[x] = scc_cnt;
            if (x == u)
                break;
        }
    }
}

void find_scc() {
    memset(pre, 0, sizeof(pre));
    memset(sccno, 0, sizeof(sccno));
    dfs_clock = scc_cnt = 0;

    for (int i = 0; i <= n; i++)
        if (!pre[i])
            dfs(i);
}

void AddEdge(int from, int to) {
    E[tot].to = to;
    E[tot].next = head[from];
    head[from] = tot++;
}

int DP(int u) {
    if (dp[u]) return dp[u];
    int Max = 0;
    for (int i = 1; i <= scc_cnt; i++)
        if (i != u && link[u][i])
            Max = max(Max, DP(i));
    return dp[u] = Max + num[u];
}

void init() {
    memset(head, -1, sizeof(head));
    tot = 0;

    scanf("%d%d", &n, &m);
    if (n == 0) {
        printf("0\n");
        return ;
    }
    int u, v;
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &u, &v);
        AddEdge(u, v);
    }
    find_scc();
    memset(link, 0, sizeof(link));
    for (u = 0; u <= n; u++) {
        for (int i = head[u]; i != -1; i = E[i].next) {
            v = E[i].to;
            if (sccno[u] != sccno[v]) link[sccno[u]][sccno[v]] = true;
        }
    }
    memset(dp, 0, sizeof(dp));
    int Max = 0;
    for (int i = 1; i <= scc_cnt; i++) {
        Max = max(Max, DP(i));
    }
    printf("%d\n", Max);
}

int main() {
    int test;
    scanf("%d", &test);
    while (test--) {
        init();
    }
    return 0;
}