洛谷-2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm

本文介绍了一种算法,用于解决奶牛在威斯康星州农场中收集万圣节糖果的问题。奶牛根据预设路线在N个牛棚隔间中穿梭,算法计算每头奶牛能到达的隔间数量,涉及图论中的缩点和记忆化搜索技术。

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

题目描述
每年,在威斯康星州,奶牛们都会穿上衣服,收集农夫约翰在N(1<=N<=100,000)个牛棚隔间中留下的糖果,以此来庆祝美国秋天的万圣节。
由于牛棚不太大,FJ通过指定奶牛必须遵循的穿越路线来确保奶牛的乐趣。为了实现这个让奶牛在牛棚里来回穿梭的方案,FJ在第i号隔间上张贴了一个“下一个隔间”Next_i(1<=Next_i<=N),告诉奶牛要去的下一个隔间;这样,为了收集它们的糖果,奶牛就会在牛棚里来回穿梭了。
FJ命令奶牛i应该从i号隔间开始收集糖果。如果一只奶牛回到某一个她已经去过的隔间,她就会停止收集糖果。
在被迫停止收集糖果之前,计算一下每头奶牛要前往的隔间数(包含起点)。
输入格式
第1行 整数n。
第2行到n+1行 每行包含一个整数 next_i 。
输出格式
n行,第i行包含一个整数,表示第i只奶牛要前往的隔间数。

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

输出样例#1:
1
2
2
3

解释:首先缩点,然后统计每个点的个数,最后我们用f[i]表示从i出发可以到达的点的个数,即直接记忆化搜索一下就好了

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 1e5 + 1e4, maxm = 1e5 + 1e4;
int Index,pd[maxn],DFN[maxn],LOW[maxn];
int tot,color[maxn],sum[maxn],f[maxn];
int edge,fir[maxn],Next[maxm],to[maxm];
int sta[maxn],top; //手写栈
int n,m,val[maxn],y[maxm],ans;
void add(int x,int y){
    to[++edge]=y; Next[edge]=fir[x]; fir[x]=edge;
}
void tarjan(int x){
    sta[++top]=x;
    pd[x]=1;
    DFN[x]=LOW[x]= ++Index;
    for(int i=fir[x];i;i=Next[i]){
        int v=to[i];
        if(!DFN[v]){
            tarjan(v);
            LOW[x]=min(LOW[x],LOW[v]);
        }
        else if(pd[v]){
            LOW[x]=min(LOW[x],DFN[v]);
        }
    }
    if(DFN[x]==LOW[x]){
        tot++;
        while(sta[top+1]!=x){
            color[sta[top]]=tot;
            pd[sta[top--]]=0;
        }
    }
}
void search(int x){
    if(f[x]) return;
    f[x]+=sum[x];
    for(int i=fir[x];i;i=Next[i]){
        int v=to[i];
        search(v);
        f[x]+=f[v];
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>y[i];
        add(i,y[i]);
    }
    for(int i=1;i<=n;i++) if(!DFN[i]) tarjan(i);
    memset(fir,0,sizeof(fir));
    memset(Next,0,sizeof(Next));
    memset(to,0,sizeof(to));
    edge=0;
    for(int i=1;i<=n;i++){
        sum[color[i]]++;
        if(color[i]!=color[y[i]])
            add(color[i],color[y[i]]);
    }
    for(int i=1;i<=tot;i++){
        search(i);
    }
    for(int i=1;i<=n;i++){
        cout<<f[color[i]]<<endl;
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值