[tarjan缩点][拓扑排序]Trick Or Treat On The Farm

本文介绍了一种算法解决方案,用于解决一个有趣的问题:如何计算每只奶牛在遵循特定路径规则的情况下能够收集到的糖果数量。通过使用Tarjan算法进行强连通分量分析,并结合拓扑排序,实现了对问题的有效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

每年万圣节,威斯康星的奶牛们都要打扮一番,出门在农场的N个牛棚里转 悠,来采集糖果.她们每走到一个未曾经过的牛棚,就会采集这个棚里的1颗糖果.

农场不大,所以约翰要想尽法子让奶牛们得到快乐.他给每一个牛棚设置了一个“后继牛 棚”.牛棚i的后继牛棚是next_i 他告诉奶牛们,她们到了一个牛棚之后,只要再往后继牛棚走去, 就可以搜集到很多糖果.事实上这是一种有点欺骗意味的手段,来节约他的糖果.

第i只奶牛从牛棚i开始她的旅程.请你计算,每一只奶牛可以采集到多少糖果.

输入输出格式

输入格式:
第一行一个正整数n(1≤n≤10^5)
接下来n行,第i行一个正整数表示第i-1号牛棚通向的牛棚

输出格式:
总共n行,第i行表示第i号牛棚开始得到的糖果数

输入输出样例

输入样例
4
1
3
2
3
输出样例
1
2
2
3

分析

首先我们知道只有n条边
然后我们发现,n条边足够组成强联通分量
所以我们用tarjan缩点
缩点以后将拓扑序列反过来继承糖果数输出即可

#include <iostream>
#include <cstdio>
#include <queue>
#define rep(i,a,b) for (i=a;i<=b;i++)
using namespace std;
int n;
int list[100001],u[100001],v[100001];
int f[100001];

int low[100001],dfn[100001],target;
int stk[100001],top;
bool instk[100001];
int d[100001],cnt;

queue<int> q;
int w;
int p[100001],ind[100001];
void tarjan(int i)
{
    low[i]=dfn[i]=++target;
    stk[++top]=i;instk[i]=1;
    if (!dfn[v[i]])
    {
        tarjan(v[i]);
        low[i]=min(low[i],low[v[i]]);
    }
    else
    if (instk[v[i]])
    low[i]=min(low[i],dfn[v[i]]);
    if (low[i]==dfn[i])
    {
        int s=0,o;
        cnt++;
        do
        {
            o=stk[top];
            top--;
            d[o]=cnt;
            instk[o]=0;
            s++;
        }
        while (i!=o);
        f[cnt]=s;
    }
}
void topsort()
{
    int i;
    rep(i,1,cnt)
    if (!ind[i]) q.push(i);
    while (!q.empty())
    {
        int k=q.front();
        q.pop();
        p[++w]=k;
        if (!(--ind[v[list[k]]])) q.push(v[list[k]]);
    }
}
void init()
{
    int i;
    scanf("%d",&n);
    rep(i,1,n)
    {
        scanf("%d",&v[i]);
        u[i]=i;
    }
}
void doit()
{
    int i;
    rep(i,1,n)
    if (!dfn[i]) tarjan(i);
    rep(i,1,n)
    if (d[i]!=d[v[i]])
    {
        u[i]=d[i];v[i]=d[v[i]];
        list[u[i]]=i;
        ind[v[i]]++;
    }
    topsort();
    for (i=w;i>=1;i--)
    f[p[i]]+=f[v[list[p[i]]]];
}
void print()
{
    int i;
    rep(i,1,n)
    printf("%d\n",f[d[i]]);
}
int main()
{
    init();
    doit();
    print();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值