题解:AT_abc357_e [ABC357E] Reachability in Functional Graph
其实可以说是基环树?
Idea
我们都知道, 个点 条边的图是树。
那么在上面加一条边,它就会产生一个环。
如果我们把本题的图画一下:
(这只是一种可能情况,不是样例。)
虽然这个图很复杂,但是它其实只由 部分组成:
(下面这个图是将对应连通块重新整排之后得到的,点的关系不变)
分析一下,无论这个连通块长什么样子,它都有一个环,环上的每个点为根牵引着一颗子树。
所以我们的第一步是找环。
找环我们可以使用拓扑排序,当然也可以直接模拟。
这里讲一讲模拟的做法:从随便一个点出发,往它的后继跳,途中记录哪个点被走到了,如果走到了已经被走到的点,则说明这个点一定在环上,从上次到这次之间被走到的点都在环上。
可以手搓一下:
对于以下的图:
我们选择 号点出发。
首先, 号点只能往 号点跳。
接着到 号点。
接着到 号点。
然后不断地沿着它的出边跳,依次经过 号、 号、 号点,然后又回到 号点。
然后就会发现 号、 号、 号、 号点都在环上了。
设这个环的长度为 ,则环上的点可通达的点数就是 。
然后对于每个环上的点进行 dfs,为了 dfs 我们需要建反图。
dfs 到了一个点,它的答案就是它父亲的答案加 。
注意可能有多个连通块,因此记录哪个点有答案了,然后多找几个环。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node {
int u,v,nxt;
} e[200005];
int n,x[200005],cnt,head[200005],huan[200005],t[200005],vis[200005],viss[200005],tot,tott,ans[200005];
void add(int u,int v) {
e[++cnt].u=u;
e[cnt].v=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void dfs(int now,int ceng) {
ans[now]=tott+ceng;
vis[now]=1;
for(int i=head[now]; i; i=e[i].nxt) {
if(!vis[e[i].v])dfs(e[i].v,ceng+1);
}
return;
}
signed main() {
cin>>n;
for(int i=1; i<=n; i++) {
cin>>x[i];
add(x[i],i);
}
for(int j=1; j<=n; j++) {
tot=0;tott=0;
while(vis[j])j++;
// cout<<j<<endl;
if(j>n)break;
t[++tot]=j;
int ii;
for(ii=j;; ii=x[ii]) {
t[++tot]=ii;
if(viss[ii])break;
viss[ii]=1;
}
// memset(viss,0,sizeof(viss));
for(int i=tot-1; i>=1; i--) {
huan[++tott]=t[i];
// cout<<huan[tott]<<' ';
if(t[i]==t[tot])break;
}
// cout<<endl;
for(int i=1; i<=tott; i++) {
ans[huan[i]]=tott;
vis[huan[i]]=1;
}
for(int i=1; i<=tott; i++) {
dfs(huan[i],0);
}
//memset(vis,0,sizeof(vis));
}
long long sum=0;
for(int i=1; i<=n; i++){
sum+=ans[i];
// cout<<ans[i]<<' ';
}
cout<<sum;
return 0;
}
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】