CF711D Directed Roads 题解

Description

洛谷传送门

Solution

看到 \(n\) 个点 \(n\) 条边,显然的基环树(可能是基环树森林),所以我们对于环上的点和非环上的点分别处理。

假设一共有 \(cnt\) 个环,每个环上有 \(d_i\) 个点,我们来分类讨论一下:

  • 对于环上的点,我们发现只有两种情况会产生环,即

    • \(1 \rightarrow 2 \rightarrow 3 \rightarrow ··· \rightarrow d_i - 1 \rightarrow d_i\)
    • \(d_i \rightarrow d_i - 1 \rightarrow ··· \rightarrow 3 \rightarrow 2 \rightarrow 1\)

    所以我们用总情况数减去 \(2\)即可。方案数:

    \[ans = \prod\limits_{i = 1}^{cnt}{(2^{d_i} - 2)} \]

  • 对于非环上的点,我们发现不论朝哪个方向连,都不会影响是否会产生环,所以方案数为 \(2^{非环上点的个数}\):

    \[ans = 2^{n - \sum\limits_{i = 1}^{cnt}d_i} \]

至此,我们就讨论完了(事实上还是很简单的),总结一下:

\[ans = \prod\limits_{i = 1}^{cnt}{(2^{d_i} - 2)} \times 2^{n - \sum\limits_{i = 1}^{cnt}d_i} \]

那么如何找环呢?我这里写了个 Tarjan 缩点,大小大于 2 的强连通分量就是环(感觉用牛刀杀鸡了……不管了)。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long

using namespace std;

const ll mod = 1e9 + 7;
const ll N = 2e5 + 10;
ll n;
struct node{
    ll v, nxt;
}edge[N];
ll head[N], tot;
ll dfn[N], low[N], tim;
ll stk[N], top, t[N];
ll cnt, siz[N];

inline void add(ll x, ll y){
    edge[++tot] = (node){y, head[x]};
    head[x] = tot;
}

void tarjan(ll x){
    low[x] = dfn[x] = ++tim;
    stk[++top] = x;
    t[x] = 1;
    for(ll i = head[x]; i; i = edge[i].nxt){
        ll y = edge[i].v;
        if(!dfn[y])tarjan(y), low[x] = min(low[x], low[y]);
        else if(t[y]) low[x] = min(low[x], dfn[y]);
    }
    if(low[x] == dfn[x]){
        cnt++;
        do{
            siz[cnt]++;
            t[stk[top--]] = 0;
        }while(stk[top + 1] != x);
    }
}

inline ll qpow(ll a, ll b){
    ll res = 1;
    while(b){
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

signed main(){
    scanf("%lld", &n);
    for(ll i = 1, x; i <= n; ++i){
        scanf("%lld", &x);
        add(i, x);
    }
    for(ll i = 1; i <= n; ++i)
        if(!dfn[i]) tarjan(i);
    ll sum = 0, ans = 1;
    for(ll i = 1; i <= cnt; ++i)
        if(siz[i] > 1) ans = ans * (qpow(2, siz[i]) - 2 + mod % mod) % mod, sum += siz[i];
    ans = ans * qpow(2, n - sum) % mod;
    printf("%lld\n", ans);
    return 0;
}

End

上一篇:排序算法原理


下一篇:[提高组集训2021] 模拟赛5