LG2921 [USACO2008DEC]Trick or Treat on the Farm 内向基环树

问题描述

LG2921


题解

发现一共有 \(n\) 个点,每个点只有一条出边,即只有 \(n\) 条边,于是就是一个内向基环树。

\(\mathrm{Tarjan}\) 缩点。

但是这个题比较猥琐的就是有自环。

所以断定一个强联通分量 \(i\) 是环的条件是 \(size_i>1\)

然后记搜求答案,特判自环。


\(\mathrm{Code}\)

#include<bits/stdc++.h>
using namespace std;

template <typename Tp>
void read(Tp &x){
    x=0;char ch=1;int fh;
    while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar();
    if(ch=='-') ch=getchar(),fh=-1;
    else fh=1;
    while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    x*=fh;
}

const int maxn=100007;

int n;
int son[maxn],fa[maxn];

int dfn[maxn],low[maxn],ind;
bool ins[maxn];
int sta[maxn],top;

int bel[maxn],cnt;
int size[maxn];
int spe;

void tarjan(int x){
    dfn[x]=low[x]=++ind,ins[x]=1,sta[++top]=x;
    if(dfn[son[x]]){
        if(ins[son[x]]) low[x]=min(low[x],dfn[son[x]]);
    }
    else{
        tarjan(son[x]);
        low[x]=min(low[x],low[son[x]]);
    }
    if(dfn[x]==low[x]){
        ++cnt;
        while(sta[top]!=x){
            bel[sta[top]]=cnt;
            ins[sta[top]]=0;--top;
            ++size[cnt];
        }
        ++size[cnt];
        ins[x]=0,bel[x]=cnt;--top;
    }
}

int ans[maxn];

int dfs(int x){
    if(size[bel[x]]>1) return ans[x]=size[bel[x]];
    if(ans[x]) return ans[x];
    return ans[x]=dfs(son[x])+1;
}

int main(){
    read(n);
    for(int i=1;i<=n;i++){
        read(son[i]);fa[son[i]]=i;
        if(son[i]==i) ans[i]=1;
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);
        if(size[bel[i]]>1) spe=bel[i];
    }
    for(int i=1;i<=n;i++){
        if(!ans[i]) dfs(i);
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/liubainian/p/11700099.html