P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm

对于一个牛,它存在两种状态:1.处于联通分量 2.不处于联通分量。对于处于联通分量的牛,求出联通分量的大小;对于不处于联通分量的牛,求出其距离联通分量的路程+联通分量大小。

不同的联通分量,染上不同的颜色,可以计算各个联通分量的大小。

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

int vis[100005];
int low[100005];
int Loop[100005],color[100005],dfn[100005],head[100005],ans[100005],st[100005], Next[100005];
int pos;
int k=1;
int col;
int top;

struct node
{
    int v,next;
}edge[100005];

void Add(int u,int v)
{
    edge[k].v = v;
    edge[k].next = head[u];
    head[u] = k++;
}

void Find(int root,int x,int step)
{
    if(ans[x]!=0)
    {
        ans[root] = ans[x]+step;
        return;
    }
    else
        Find(root,Next[x],step+1);
}

void Tarjan(int u)
{
    dfn[u] = low[u] = ++pos;
    st[++top] = u;
    vis[u] = 1;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v = edge[i].v;
        if(!dfn[v])
        {
            Tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(vis[v])
        {
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(dfn[u] == low[u])
    {
        col++;
        int x;
        do{
            x = st[top--];
            vis[x] = 0;
            color[x] = col;
        }while(x!=u);
    }
}

int main()
{
    memset(head,-1,sizeof(head));
    int n,v;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&v);
        Next[i] = v;
        Add(i,v);
        if(v==i)
            ans[i] = 1;
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
        {
            Tarjan(i);
        }
    }
    for(int i=1;i<=n;i++)
    {
        Loop[color[i]]++;
    }
    for(int i=1;i<=n;i++)
    {
        if(Loop[color[i]]!=1)
            ans[i] = Loop[color[i]];
    }
    for(int i=1;i<=n;i++)
    {
        if(ans[i]==0)
            Find(i,Next[i],1);
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d
",ans[i]);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/alan-W/p/10756744.html