Loading

信息传递 题解

信息传递 题解

题目给了一堆有向边\((i,T_i)\),要求最小环

因为每个点的出度为1,所以每个点只可能在一个环上。那么tarjan就好了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned int uint;
#define rint register int
#define pb push_back
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2) EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
inline int rd() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return x*f;
}
const int N=2e5+10;
int n,ans=1<<28;
int st[N],top,timer,dfn[N],low[N],scc[N],C;
int num_edge,head[N];
struct edge {
    int to,nxt;
}e[N];
void addedge(int from,int to) {
    ++num_edge;
	e[num_edge].nxt=head[from];
	e[num_edge].to=to;
	head[from]=num_edge;
}
void tarjan(int u) {
	low[u]=dfn[u]=++timer,st[++top]=u;
	for(rint 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(!scc[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]) {
		scc[u]=++C;int siz=1;
		while(st[top]!=u)scc[st[top--]]=C,++siz;
		--top;
		if(siz>1)ans=min(ans,siz);
	}
}
signed main() {
	n=rd();
	for(rint i=1;i<=n;++i)addedge(i,rd());
	for(rint i=1;i<=n;++i)if(!dfn[i])tarjan(i);
	printf("%d\n",ans);
	return 0;
}

当然了,用并查集也能做,但是一定要带路径压缩或启发式合并。有些人写法伪了还能AC。具体见https://www.luogu.com.cn/discuss/show/236216

因为不带路径压缩复杂度错了,所以路径压缩必须带。但是一旦路径压缩就不能通过遍历节点到根的距离来统计答案。那么必须直接把值存下来,在路径压缩前,递归,通过父亲来更新自己的权值(好像叫什么扩展域并查集???)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned int uint;
#define rint register int
#define pb push_back
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2) EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
inline int rd() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return x*f;
}
const int N=200010;
int n,fa[N],dis[N],ans=1<<28;
int find(int x) {
	if(x==fa[x])return x;
	int fx=fa[x];//先存一下,不然没法找
	fa[x]=find(fa[x]);//先把父亲更新对了
	dis[x]+=dis[fx];//通过父亲更新自己
	return fa[x];
}
void merge(int x,int y) {
	int fx=find(x),fy=find(y);
	if(fx==fy)ans=min(ans,dis[y]+1);
	else fa[fx]=fy,dis[x]=dis[y]+1;//x接在y下面,同时合并并查集
}
signed main() {
	n=rd();
	for(rint i=1;i<=n;++i)fa[i]=i;
	for(rint i=1;i<=n;++i)merge(i,rd());
	printf("%d\n",ans);
	return 0;
} 
posted @ 2020-07-09 12:26  zzctommy  阅读(146)  评论(0编辑  收藏  举报