[CF22E]Scheme

Scheme

题解

看到了强连通图,很容易想到了缩点。由于边数是等于点数的,所以我们缩完点得到的一定是一片森林。

接下来需要我们yy出一种构造方法。

首先,对于任意一个叶子节点,它肯定需要至少一条边成为它的入边,而对于任意一个根节点,它也至少需要一条边成为它的出边。由于一条边只能带来一个入边与一个出边,且叶子节点数量大于根节点,所以最后的答案即加边的数量肯定是叶子节点的个数。

可我们又该如何去构造它呢。由于每个根节点数相对来说较少,我们可以先将它们与一个叶子节点相匹配,但必须要保证整个图的连通,所以我们可以依次将一个叶子节点连到下一棵树的根节点上,而这棵树的其它叶子节点,接到它上面即可。

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x7f7f7f7f7f7f7f7f;
const int mo=1e9+7;
typedef pair<LL,LL> pii;
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int dfn[MAXN],low[MAXN],sta[MAXN],stak;
int n,tot,head[MAXN],idx,cnt,fa[MAXN];
int belong[MAXN],pre[MAXN],sum,Fa[MAXN];
int din[MAXN],dout[MAXN],ans;
bool insta[MAXN];
queue<int> q;
vector<int> G[MAXN],vec;
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
int findSet(int x){return Fa[x]==x?x:Fa[x]=findSet(Fa[x]);}
void unionSet(int a,int b){
	int u=findSet(a),v=findSet(b);
	if(u==v)return ;Fa[u]=v;
}
void tarjan(int u){
	dfn[u]=low[u]=++idx;sta[++stak]=u;insta[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
		else if(insta[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		cnt++;int v;pre[cnt]=u;
		do{
			v=sta[stak--];
			belong[v]=cnt;insta[v]=0;
		}while(u!=v);
	}
}
signed main(){
	read(n);
	for(int i=1;i<=n;i++)read(fa[i]),addEdge(i,fa[i]);
	for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
	//for(int i=1;i<=n;i++)printf("%d:%d %d\n",i,belong[i],fa[i]);
	for(int i=1;i<=cnt;i++)Fa[i]=i;
	for(int i=1;i<=n;i++)
		if(belong[fa[i]]!=belong[i]){
			//printf("%d %d %d %d\n",i,fa[i],belong[i],belong[fa[i]]);
			din[belong[fa[i]]]++,dout[belong[i]]++,
			unionSet(belong[i],belong[fa[i]]);
		}
	if(cnt==1){puts("0");return 0;}
	for(int i=1;i<=cnt;i++)if(!din[i])G[findSet(i)].push_back(i);
	for(int i=1;i<=cnt;i++)if(!dout[i])sum++,ans+=G[i].size(),vec.push_back(i);
	printf("%d\n",ans);
	for(int i=1;i<=vec.size();i++){
		int siz=G[vec[i-1]].size();
		for(int j=1;j<siz;j++)
			printf("%d %d\n",pre[G[vec[i-1]][j-1]],pre[G[vec[i-1]][j]]);
		if(i<vec.size())printf("%d %d\n",pre[vec[i-1]],pre[G[vec[i]][0]]);
		else printf("%d %d\n",pre[vec[i-1]],pre[G[vec[0]][0]]);
	}
	return 0;
}

谢谢!!!

posted @   StaroForgin  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示