信息传递 题解
信息传递 题解
题目给了一堆有向边\((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;
}
路漫漫其修远兮,吾将上下而求索