2 条题解

  • 1
    @ 2026-10-5 16:01:55

    2772. 环

    简要题意

    nn 个节点 mm 条边的有向图,求有多少个节点 uu 满足 存在一个包含 uu 在内的环,环长对 33 取模时 00

    n≤200000,m≤400000n\le200000,m\le400000

    思路

    看到了 有向图、环、n≤200000n \le 200000,就可以联想到强联通分量。

    考虑对每一个强联通分量依次处理,很显然,不可能有一个环跨过强连通分量,所以这样不会少算可能性。

    强连通分量的定义是:满足任意两个节点可以互相到达的极大子图。

    所以一个强联通分量中,只要一个节点被以 xx 和 x+1x+1 两个计数器访问,那么这个节点就应该加入贡献。

    然后这道题目就做完了,有什么疑问或写的不清晰的地方欢迎提出

    最后放一份代码

    #include <bits/stdc++.h>
    using namespace std;
    using uint = unsigned int;
    using ll = long long;
    using ull = unsigned long long;
    constexpr size_t N = 200005, M = 400005;
    int n, m;
    int head[N], nxt[M], to[M], cntg;
    int dfn[N], low[N], sta[N], belong[N], cntd, top, scc;
    bool vis[N][3];//vis[u][c]表示u是否被已c的计数器访问过
    int res;
    void addEdge(int u, int v)
    {
        nxt[++cntg] = head[u];
        to[cntg] = v;
        head[u] = cntg;
    }
    void tarjan(int u)//强连通分量
    {
        dfn[u] = low[u] = ++cntd;
        sta[++top] = u;
        for (int e = head[u]; e > 0; e = nxt[e])
        {
            int v = to[e];
            if (dfn[v] == 0)
            {
                tarjan(v);
                low[u] = min(low[u], low[v]);
            }
            else if (belong[v] == 0)
            {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u])
        {
            int v;
            scc++;
            do
            {
                v = sta[top--];
                belong[v] = scc;
            } while (v != u);
        }
    }
    void dfs(int u, int c)
    {
        if (vis[u][c]) return;
        vis[u][c] = true;
        for (int e = head[u]; e > 0; e = nxt[e])
        {
            int v = to[e];
            if (belong[u] == belong[v])//保证在同一个强连通分量中
            {
                dfs(v, (c == 2 ? 0 : c + 1));
            }
        }
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cin >> n >> m;
        int u, v;
        for (int i = 1; i <= m; i++)
        {
            cin >> u >> v;
            addEdge(u, v);
        }
        for (int i = 1; i <= n; i++)
        {
            if (dfn[i] == 0)
            {
                tarjan(i);
            }
            if (!vis[i][0] && !vis[i][1] && !vis[i][2])//这个节点的强连通分量没有被dfs过
            {
                dfs(i, 0);
            }
        }
        for (int i = 1; i <= n; i++)
        {
            if ((vis[i][0] && vis[i][1]) || (vis[i][1] && vis[i][2]) || (vis[i][2] && vis[i][0]))
            {
                //这个节点被以两个相差1的计数器访问了,答案+1
                res++;
            }
        }
        cout << res << '\n';
        return 0;
    }
    

    信息

    ID
    2772
    时间
    2000ms
    内存
    128MiB
    难度
    6
    标签
    (无)
    递交数
    41
    已通过
    8
    上传者