信息传递
[TIMEGATE]
https://www.luogu.org/problem/P2661
【解题思路】
把每个同学看成一个点,信息的传递就是在他们之间连有向边,游戏轮数就是求最小环。
图论求最小环,我在里面看到了并查集。
假如说信息由A传递给B,那么就连一条由A指向B的边,同时更新A的父节点,A到它的父节点的路径长也就是B到它的父节点的路径长+1。
这样我们就建立好了一个图,之后信息传递的所有环节都按照这些路径。游戏结束的轮数,也就是这个图里最小环的长度。
如果有两个点祖先节点相同,那么就可以构成一个环,长度为两个点到祖先节点长度之和+1。
和下面的并查集有点不一样的。
【code】
1 #include <cstdio> 2 #include <cstring> 3 #include <stack> 4 using namespace std; 5 const int N=200005; 6 int next[N],head[N],end[N],vis[N],dfn[N],low[N]; 7 int i,n,tt,t,a,ans=1<<30,sum,v; 8 stack<int> s,ss; 9 void add(int u,int v){ 10 next[++t]=head[u]; 11 head[u]=t; 12 end[t]=v; 13 } 14 void tarjan(int u){ 15 int i; 16 tt++; 17 dfn[u]=tt; 18 low[u]=tt; 19 s.push(u); 20 vis[u]=1; 21 for(i=head[u];i;i=next[i]){ 22 v=end[i]; 23 if(dfn[v]==-1){ 24 tarjan(v); 25 low[u]=min(low[u],low[v]); 26 } 27 else if(vis[v]==1) 28 low[u]=min(low[u],dfn[v]); 29 } 30 if(dfn[u]==low[u]){ 31 sum=0; 32 while(1){ 33 v=s.top(); 34 s.pop(); 35 sum++; 36 vis[v]=0; 37 if(u==v)break; 38 } 39 if(sum>1&&sum<ans)ans=sum; 40 } 41 } 42 int main(){ 43 //freopen("message.in","r",stdin); 44 //freopen("message.out","w",stdout); 45 memset(dfn,-1,sizeof(dfn)); 46 scanf("%d",&n); 47 for(i=1;i<=n;i++){ 48 scanf("%d",&a); 49 add(i,a); 50 } 51 for (i=1;i<=n;i++) 52 if(dfn[i]==-1) 53 tarjan(i); 54 printf("%d",ans); 55 return 0; 56 }