题解:AT_abc357_e [ABC357E] Reachability in Functional Graph

其实可以说是基环树?

Idea

我们都知道,nn 个点 n1n-1 条边的图是树。

那么在上面加一条边,它就会产生一个环。

如果我们把本题的图画一下:

(这只是一种可能情况,不是样例。)

虽然这个图很复杂,但是它其实只由 22 部分组成:

(下面这个图是将对应连通块重新整排之后得到的,点的关系不变)

分析一下,无论这个连通块长什么样子,它都有一个环,环上的每个点为根牵引着一颗子树。

所以我们的第一步是找环。

找环我们可以使用拓扑排序,当然也可以直接模拟。

这里讲一讲模拟的做法:从随便一个点出发,往它的后继跳,途中记录哪个点被走到了,如果走到了已经被走到的点,则说明这个点一定在环上,从上次到这次之间被走到的点都在环上。

可以手搓一下:

对于以下的图:

我们选择 66 号点出发。

首先,66 号点只能往 44 号点跳。

接着到 55 号点。

接着到 11 号点。

然后不断地沿着它的出边跳,依次经过 88 号、99 号、77 号点,然后又回到 11 号点。

然后就会发现 11 号、88 号、99 号、77 号点都在环上了。

设这个环的长度为 qq,则环上的点可通达的点数就是 qq

然后对于每个环上的点进行 dfs,为了 dfs 我们需要建反图。

dfs 到了一个点,它的答案就是它父亲的答案加 11

注意可能有多个连通块,因此记录哪个点有答案了,然后多找几个环。

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;
}
posted @   Weslie_qwq  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示