最平凡的方式AC
查看原帖
最平凡的方式AC
1423898
dct_strange楼主2024/12/20 18:14

代码如下

#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 1e5 + 5; // 最大点数
int MOD; // 取模
int n, m;             // 节点数和边数

vector<int> graph[N];  // 原图的邻接表
vector<int> dag[N];    // 缩点后的DAG图
int dfn[N], low[N], scc[N], sccSize[N], d[N], ways[N]; // Tarjan和动态规划需要的数组
bool inStack[N];       // 判断点是否在栈中
stack<int> st;         // Tarjan算法的栈
int timestamp = 0, sccCount = 0; // 时间戳和强连通分量计数

// Tarjan算法:寻找强连通分量
void tarjan(int u) {
    dfn[u] = low[u] = ++timestamp;
    st.push(u);
    inStack[u] = true;
    for (int v : graph[u]) {
        if (!dfn[v]) { // 未访问过
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (inStack[v]) { // 在当前栈中
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (dfn[u] == low[u]) { // 找到一个强连通分量
        sccCount++;
        int node;
        do {
            node = st.top();
            st.pop();
            inStack[node] = false;
            scc[node] = sccCount; // 记录点所属的SCC编号
            sccSize[sccCount]++;  // 统计SCC的大小
        } while (node != u);
    }
}

// 在DAG上进行深度优先搜索,寻找最长路径,并统计方案数
int dfs(int u) {
    if (d[u]) return d[u]; // 如果已经计算过,直接返回
    d[u] = sccSize[u]; // 起始点的路径长度
    ways[u] = 1;       // 起始点的方案数为 1
    for (int v : dag[u]) {
        int len = dfs(v) + sccSize[u]; // 计算路径长度
        if (len > d[u]) {
            d[u] = len;
            ways[u] = ways[v]; // 更新方案数
        } else if (len == d[u]) {
            ways[u] = (ways[u] + ways[v]) % MOD; // 累加方案数,取模
        }
    }
    return d[u];
}

int main() {
    cin >> n >> m >>MOD;

    // 读取图的输入(邻接表存储)
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        graph[u].push_back(v);
    }

    // 使用Tarjan算法寻找强连通分量
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) {
            tarjan(i);
        }
    }

    // 构建缩点后的DAG
    for (int u = 1; u <= n; u++) {
        for (int v : graph[u]) {
            if (scc[u] != scc[v]) { // 不在同一个SCC中才加边
                dag[scc[u]].push_back(scc[v]);
            }
        }
    }

    // 对DAG的边去重(最平凡的方式实现)
    for (int i = 1; i <= sccCount; i++) {
        // 排序确保重复元素相邻
        sort(dag[i].begin(), dag[i].end());

        // 手动去重:保留唯一元素
        vector<int> uniqueEdges;
        for (int j = 0; j < dag[i].size(); j++) {
            if (j == 0 || dag[i][j] != dag[i][j - 1]) { // 和前一个元素不同才保留
                uniqueEdges.push_back(dag[i][j]);
            }
        }
        dag[i] = uniqueEdges; // 将去重后的结果赋回原来的邻接表
    }

    // 在DAG上计算最长路径及方案数
    int maxLength = 0, totalWays = 0;
    memset(d, 0, sizeof(d));
    memset(ways, 0, sizeof(ways));
    for (int i = 1; i <= sccCount; i++) {
        int len = dfs(i); // 计算每个 SCC 的最长路径
        if (len > maxLength) {
            maxLength = len;
            totalWays = ways[i]; // 更新方案数
        } else if (len == maxLength) {
            totalWays = (totalWays + ways[i]) % MOD; // 累加方案数,取模
        }
    }

    // 输出最长路径长度和方案数
    cout << maxLength << endl;
    cout << totalWays << endl;
    return 0;
}
2024/12/20 18:14
加载中...