洛谷 P2921 [USACO08DEC]Trick or Treat on the Farm G 题解

题目链接

题目大意

每年,在威斯康星州,奶牛们都会穿上衣服,收集农夫约翰在N(1<=N<=100000)个牛棚隔间中留下的糖果,以此来庆祝美国秋天的万圣节。

由于牛棚不太大,FJ通过指定奶牛必须遵循的穿越路线来确保奶牛的乐趣。为了实现这个让奶牛在牛棚里来回穿梭的方案,FJ在第i号隔间上张贴了一个“下一个隔间”Next_i(1<=Next_i<=N),告诉奶牛要去的下一个隔间;这样,为了收集它们的糖果,奶牛就会在牛棚里来回穿梭了。

FJ命令奶牛i应该从i号隔间开始收集糖果。如果一只奶牛回到某一个她已经去过的隔间,她就会停止收集糖果。

在被迫停止收集糖果之前,计算一下每头奶牛要前往的隔间数(包含起点)。

题解

此题也可以用tarjan缩点
由题意可得每个牛到了一个牛棚后下一个只能去后继牛棚

那么我们可以用tarjan先预处理出来,然后再用记忆化搜索统计答案

一定要用记忆化搜索,否则会超时6个点

代码

#include < cstdio >
#include < cstring >
#include < queue >
#include < algorithm >
using namespace std;
const int maxn=200005;
int n,x,k=0,head[maxn],dfn[maxn],low[maxn],color_num=0,dfs_num=0;
bool visit[maxn];int ans[maxn];
int stack[maxn],top=0,val[maxn],head_new[maxn],color[maxn];
struct node
{
    int to,next;
} edge[maxn<<1],edge_new[maxn<<1];
void add(int u,int v)
{
    edge[++k].to=v;
    edge[k].next=head[u];
    head[u]=k;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++dfs_num;
    visit[x]=true;stack[++top]=x;
    for(int i=head[x];i;i=edge[i].next)
    {
        if(!dfn[edge[i].to])
        {
            tarjan(edge[i].to);
            low[x]=min(low[x],low[edge[i].to]);
        }
        else if(visit[edge[i].to]) low[x]=min(low[x],dfn[edge[i].to]);
    }
    if(low[x]==dfn[x])
    {
        visit[x]=false;color_num++;
        while(stack[top+1]!=x)
        {
            val[color_num]++;
            visit[stack[top]]=false;
            color[stack[top]]=color_num;
            top--;
        }
    }
}

void add_new(int u,int v)
{
    edge_new[++k].to=v;
    edge_new[k].next=head_new[u];
    head_new[u]=k;
}
void dfs(int x)
{
    if(ans[x]) return;
    ans[x]+=val[x];
    for(int i=head_new[x];i;i=edge_new[i].next)
    {
        dfs(edge_new[i].to);
        ans[x]+=ans[edge_new[i].to];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,x);
    }
    k=0;
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];j;j=edge[j].next)
        {
            if(color[i]!=color[edge[j].to])
            {
                add_new(color[i],color[edge[j].to]);
            }
        }
    }
    for(int i=1;i<=n;i++) dfs(color[i]);
    for(int i=1;i<=n;i++) printf("%d\n",ans[color[i]]);
    return 0;
}

你可能感兴趣的:(算法,dfs,icpc)