2 条题解

  • 1
    @ 2026-10-5 23:39:10

    走出的回路一定在 scc 中,因此使用 tarjan 求出 scc。

    复杂度困境

    从每一个点都做一遍 bfs 会超时。

    期望只从 scc 的一个点出发做 bfs,即可判断 scc 中的所有点。

    破局:“以点代面”

    发现如下性质。 从 scc 内的任意一点 uu 可以走出一条合法的回路 ⇔\Leftrightarrow scc 内所有点都可以走出一条合法的回路。

    充分性: 存在一条从 uu 回到 uu 的合法回路,回路长度 L mod 3=1L \bmod 3 = 1。 从任意一个其他点 vv 可以首先走到这个回路上,设路径长度为 l1l_1;在这个回路上绕 kk 圈,路径长度为 kLkL;最后离开回路,回到点 vv,设路径长度为 l2l_2。 从 vv 到 vv 的回路合法,等价于 (l1+l2+kL) mod 3=(l1+l2+k) mod 3=1(l_1+l_2+kL) \bmod 3 = (l_1+l_2+k) \bmod 3 =1。 可以发现,不管 l1+l2l_1 + l_2 在模 3 意义下为多少,都可以改变 kk 的值使等式成立。充分性得证。

    必要性:由命题直接得到。

    我们从每个 scc 的任意点做分层图 bfs,判断这个点是否可以走一条合法的回路。如果可以,则将答案加上当前 scc 的大小。

    代码实现

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int, int> pii;
    const int N = 2e5 + 10, M = 4e5 + 10;
    struct Edge{
        int to, nxt;
    }edge[M];
    int n, m;
    int head[N], tot;
    int nowdfn, dfn[N], low[N], stk[N], tp; 
    bool instk[N];
    int color[N], nowscc; 
    vector<int> scc[N];
    bool vis[N][3];
    void add(int u, int v){
        edge[++tot].to = v;
        edge[tot].nxt = head[u];
        head[u] = tot;
    }
    void tarjan(int u){
        dfn[u] = low[u] = ++nowdfn;
        stk[++tp] = u, instk[u] = 1;
        for(int i = head[u]; i; i = edge[i].nxt){
            int v = edge[i].to;
            if(!dfn[v]){
                tarjan(v);
                low[u] = min(low[u], low[v]);
            }else if(instk[v]){//ancestor
                low[u] = min(low[u], dfn[v]);
            }
        }
        if(dfn[u] == low[u]){
            ++nowscc;
            int p = 0;
            do{
                p = stk[tp--];
                scc[nowscc].push_back(p);
                instk[p] = 0;
                color[p] = nowscc;
            }while(tp!=0 && p != u);
        }
    }
    void bfs(int st){
        queue<pii> q;
        q.push({st, 0});
        vis[st][0] = 1;
        while(!q.empty()){
            int u = q.front().first, r = q.front().second;
            q.pop();
            for(int i = head[u]; i; i = edge[i].nxt){
                int v = edge[i].to;
                if(!vis[v][(r+1)%3]){
                    vis[v][(r+1)%3] = 1;
                    q.push({v, (r+1)%3});
                }
            }
        }
    }
    int main(){
        ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
        cin >> n >> m;
        for(int i = 1, u, v; i <= m; i ++){
            cin >> u >> v;
            add(u, v);
        }
        for(int i = 1; i <= n; i ++){
            if(!dfn[i]) tarjan(i);
        }
        int ans = 0;
        for(int i = 1; i <= nowscc; i ++){//scc
            bfs(scc[i][0]);
            if(vis[scc[i][0]][1]) ans += scc[i].size();
        }
        cout << ans;
        return 0;
    }
    

    信息

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