CF711D Directed Roads 题解
Description
Solution
看到 nn 个点 nn 条边,显然的基环树(可能是基环树森林),所以我们对于环上的点和非环上的点分别处理。
假设一共有 cntcnt 个环,每个环上有 didi 个点,我们来分类讨论一下:
-
对于环上的点,我们发现只有两种情况会产生环,即
- 1→2→3→···→di−1→di→11→2→3→⋅⋅⋅→di−1→di→1
- di→di−1→···→3→2→1→didi→di−1→⋅⋅⋅→3→2→1→di
所以我们用总情况数减去 22即可。方案数:
ans=cnt∏i=1(2di−2)ans=cnt∏i=1(2di−2) -
对于非环上的点,我们发现不论朝哪个方向连,都不会影响是否会产生环,所以方案数为 2非环上点的个数2非环上点的个数:
ans=2n−cnt∑i=1dians=2n−cnt∑i=1di
至此,我们就讨论完了(事实上还是很简单的),总结一下:
ans=cnt∏i=1(2di−2)×2n−cnt∑i=1dians=cnt∏i=1(2di−2)×2n−cnt∑i=1di
那么如何找环呢?我这里写了个 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;
}
_EOF__EOF_
本文作者:xixike
本文链接:https://www.cnblogs.com/xixike/p/15497096.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
,
,
,
标签:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步