At357 E - Reachability in Functional Graph

E.Reachability in Functional Graph


原题链接

题意简述

给定一张由若干个内向基环树组成的图,求解每个点可到达点的数量之和。

解题思路

因为是内向基环树森林,所以可以先用拓扑排序预处理出所有环上的点,
单独考虑每两对可达点可能的情况,
1.两个点都在环上
2.两个点一个在环上,一个不在环上
3.两个点都不在环上
对于第一种情况,单独考虑每一个环,产生环的大小的平方的贡献,对于第二种情况的贡献时环的大小乘以环上树的大小,第三种情况的贡献是所有树的大小的和

AC code

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=2e5+5;
int to[N],in[N],f[N];
bool inC[N],vis[N];
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int n;cin>>n;
    for(int i=1;i<=n;i++) cin>>to[i],in[to[i]]++,inC[to[i]]=1;
    ll ans=0;
    //拓扑排序(标记当前是否成环+第三类贡献)
    queue<int>Q;
    for(int i=1;i<=n;i++) if(!in[i]) Q.push(i);
    while(!Q.empty()){
        int v=Q.front();
        int u=to[v];
        Q.pop();
        inC[v]=0;
        ++f[v];
        ans+=f[v];
        f[u]+=f[v];
        if(--in[u]==0){
            Q.push(u);
        }
    }
    //统计第一类和第二类贡献
    for(int i=1;i<=n;i++){
        if(vis[i]||!inC[i]) continue;
        int v=i,u=to[i];
        ll sum=0,cnt=0;
        while(!vis[v]){
            vis[v]=1;
            sum+=f[v];
            ++cnt;
            v=to[v];
        }
        ans+=cnt*cnt;//统计环上贡献
        ans+=cnt*sum;//统计环和树的贡献
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2025-05-23 14:59  usedchang  阅读(21)  评论(0)    收藏  举报