0
点赞
收藏
分享

微信扫一扫

洛谷 P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm tarjan算法


题目链接:

​​https://www.luogu.org/problem/P2921​​

算法:

tarjan

注意:

1:low[maxn]存:同一个连通分量中可以回到的节点的最小时间戳,即dfn的值

思路:

1:这道题目的本质就是tarjan算法

2:如果开始时这个节点就是在一个节点数大于2的环中,即一个连通分量中那么所要求的间隔数ans[i]就等于这个连通分量所包含的节点个数

3:如果这个节点的下一个节点是在一个环中,那么

if(ans[next[u]]) return ans[u]=ans[next[u]]+1;

4:如果一个节点的下一个节点不是在一个环中,则递归调用findans函数

else return ans[u]=findans(next[u])+1;

5:如果这个节点是自环,就是自己到达自己,那么

if(next[u]==u) return ans[u]=1;

6:2是提前处理的,3,4,5是一个递归函数,注意一定要把一开始就是在一个环中的节点处理之后再循环调用findans函数

7:1:color[maxn]存:每一个节点所处的连通分量的编号,可以理解为每一个节点染色的颜色

     2:colorcnt记录此时的连通分量的编号,即此时要染色的颜色

     3:colornum[maxn]存:每一个编号的连通分量所包含的节点个数,即每一种颜色的节点的个数,即每一个环,每一个连通分量所包含的节点个数

#include <bits/stdc++.h>

using namespace std;
//struct edge{
//int to,val;
//};
//vector<int>g[maxn];
const int maxn=1e5+5;
int next[maxn];
int dfn[maxn],low[maxn],timing;
bool ins[maxn];
int color[maxn],colornum[maxn],colorcnt;
stack<int>s;
int ans[maxn];

int findans(int u){
if(next[u]==u) return ans[u]=1;
if(ans[next[u]]) return ans[u]=ans[next[u]]+1;
else return ans[u]=findans(next[u])+1;
}

void tarjan(int u){
timing++;
dfn[u]=low[u]=timing;
s.push(u);
ins[u]=true;
// for(int v:g[u]){
int v=next[u];
if(dfn[v]==0){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]==true){
low[u]=min(low[u],dfn[v]);
}
// }
if(dfn[u]==low[u]){
colorcnt++;
while(s.top()!=u){
int temp=s.top();
s.pop();
ins[temp]=false;
color[temp]=colorcnt;
colornum[colorcnt]++;
}
s.pop();
color[u]=colorcnt;
colornum[colorcnt]++;
ins[u]=false;
}
}

int main()
{
int n,x;
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
next[i]=x;
// g[i].push_back(x);
// g[y].push_back(x);
}
for(int i=1;i<=n;i++){
if(dfn[i]==0){
tarjan(i);
}
}
for(int i=1;i<=n;i++){
if(colornum[color[i]]>1){
ans[i]=colornum[color[i]];
}
// else{
// findans(i);
// }
}
// for(int i=1;i<=n;i++){
// cout<<ans[i]<<endl;
// }
for(int i=1;i<=n;i++){
if(ans[i]==0){
findans(i);
}
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<endl;
}
return 0;
}

 

 

 

 

 

 

 

 

 

举报
0 条评论