• CF711D Directed Roads 题解


    Description

    洛谷传送门

    Solution

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

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

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

      • (1 ightarrow 2 ightarrow 3 ightarrow ··· ightarrow d_i - 1 ightarrow d_i ightarrow 1)
      • (d_i ightarrow d_i - 1 ightarrow ··· ightarrow 3 ightarrow 2 ightarrow 1 ightarrow d_i)

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

      [ans = prodlimits_{i = 1}^{cnt}{(2^{d_i} - 2)} ]

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

      [ans = 2^{n - sumlimits_{i = 1}^{cnt}d_i} ]

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

    [ans = prodlimits_{i = 1}^{cnt}{(2^{d_i} - 2)} imes 2^{n - sumlimits_{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
    ", ans);
        return 0;
    }
    

    [\_EOF\_ ]

    本文来自博客园,作者:xixike,转载请注明原文链接:https://www.cnblogs.com/xixike/p/15497096.html

  • 相关阅读:
    博客样式备份
    2018年终总结
    技术博客的太监
    LeetCode 日常填坑
    互联网之父
    TotoiseSVN的使用方法
    常用CMD命令
    量化策略
    浏览器加载js的阻塞与非阻塞
    Vue核心之数据劫持
  • 原文地址:https://www.cnblogs.com/xixike/p/15497096.html
Copyright © 2020-2023  润新知