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

此题描述了万圣节期间,奶牛们通过访问不同的牛棚收集糖果的游戏。利用Tarjan算法寻找强连通分量并计算每只奶牛能访问的独特牛棚数量。

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

 

题目描述


Every year in Wisconsin the cows celebrate the USA autumn holiday of Halloween by dressing up in costumes and collecting candy that Farmer John leaves in the N (1 <= N <= 100,000) stalls conveniently numbered 1..N.


Because the barn is not so large, FJ makes sure the cows extend their fun by specifying a traversal route the cows must follow. To implement this scheme for traveling back and forth through the barn, FJ has posted a 'next stall number' next_i (1 <= next_i <= N) on stall i that tells the cows which stall to visit next; the cows thus might travel the length of the barn many times in order to collect their candy.


FJ mandates that cow i should start collecting candy at stall i. A cow stops her candy collection if she arrives back at any stall she has already visited.


Calculate the number of unique stalls each cow visits before being forced to stop her candy collection.


POINTS: 100


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


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


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


输入输出格式


输入格式:
Line 1: A single integer: N


Lines 2..N+1: Line i+1 contains a single integer: next_i
输出格式:
Lines 1..N: Line i contains a single integer that is the total number of unique stalls visited by cow i before she returns to a stall she has previously visited.
输入输出样例


输入样例#1:





输出样例#1:




说明


Four stalls.


Stall 1 directs the cow back to stall 1.


Stall 2 directs the cow to stall 3


Stall 3 directs the cow to stall 2


Stall 4 directs the cow to stall 3
Cow 1: Start at 1, next is 1. Total stalls visited: 1.


Cow 2: Start at 2, next is 3, next is 2. Total stalls visited: 2. Cow 3: Start at 3, next is 2, next is 3. Total stalls visited: 2. Cow 4: Start at 4, next is 3, next is 2, next is 3. Total stalls visited: 3.


蛮容易上手的一道题,拿到手画一下样例就有思路

Pascal代码思路:先tarjan找环,由于一个点只有一个后继,所以如果一个点在一个环内,那么答案就是这个环所包含的点的数量,
如果不在就把这个点的答案+1然后去找它的后继直到找到一个环为止,用记忆化搜索去找它的后继,不然会超时

 

注意本题有自环,要注意对自环的处理。

 

var
    n,l,time,top,x,p,q    :longint;
    i                     :longint;
    size                  :array[0..200010] of longint;
    low,dfn,z,belong,last :array[0..100010] of longint;
    ans                   :array[0..100010] of longint;
    vis                   :Array[0..100010] of boolean;
    other,pre             :array[0..200010] of longint;
function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;

procedure connect(x,y:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
end;

procedure dfs(x:longint);
var
    cur:longint;
    p,q:longint;
begin
   inc(time);
   dfn[x]:=time;
   low[x]:=time;
   vis[x]:=true;
   inc(top);
   z[top]:=x;
   //
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if dfn[p]=0 then
      begin
         dfs(p);
         low[x]:=min(low[x],low[p]);
      end else
      if vis[p] then low[x]:=min(low[x],dfn[p]);
      q:=pre[q];
   end;
   //
   if (dfn[x]=low[x]) then
   begin
      cur:=-1;
      while (cur<>x) do
      begin
         cur:=z[top];
         dec(top);
         vis[cur]:=false;
         belong[cur]:=x;
         inc(size[belong[cur]]);
      end;
   end;
end;

function find(x:longint):longint;
begin
   if (ans[x]<>0) then exit(ans[x]);
   ans[x]:=find(other[last[x]])+1;
   exit(ans[x]);
end;
//

begin
   read(n);
   for i:=1 to n do
   begin
      read(x);
      connect(i,x);
   end;
   for i:=1 to n do if dfn[i]=0 then dfs(i);
   //
   for i:=1 to n do
    if (size[belong[i]]>1) then ans[i]:=size[belong[i]] else
     if (other[last[i]]=i) then ans[i]:=1;
   //
   for i:=1 to n do
    if (ans[i]=0) then ans[i]:=find(i);
   //
   for i:=1 to n do writeln(ans[i]);
end.

C思路:tarjan将强连通分量缩点,并记录每个强连通分量的大小,然后重构图,对新图进行dfs加记忆化剪枝

#include<stdio.h>
#include<string.h>
#define maxn 100010

int l = 0;
int n;
int link[maxn], dfn[maxn], low[maxn], belong[maxn];
int time = 0, top = 0;
int z[maxn], vis[maxn];
int last[maxn * 2], pre[maxn * 2], other[maxn * 2];
int size[maxn * 2],ans[maxn * 2];

int min(int x, int y){
    return((x) < (y) ? (x) : (y));
}

void connect(int x, int y){
    l++;
    pre[l] = last[x];
    last[x] = l;
    other[l] = y;
}

void dfs(int x) {
    int p, q;
    int cur;

    time++;
    low[x] = time;
    dfn[x] = time;
    vis[x] = 1;
    top++;
    z[top] = x;
    //
    q = last[x];
    while (q != 0) {
        p = other[q];
        if (dfn[p] == 0) {
            dfs(p);
            low[x] = min(low[x], low[p]);
        } else if (vis[p] == 1) low[x] = min(low[x], dfn[p]);
        q = pre[q];
    }
    //
    if (low[x] == dfn[x]) {
        cur = -1;
        while (cur != x) {
            cur = z[top];
            belong[cur] = n + x;
            vis[cur] = 0;
            size[belong[cur]]++;
            top--;
        }
    }
}

void dfs2(int x){
    int p, q;
    q = last[x];
    while (q != 0) {
        p = other[q];
        if (ans[p] == 0) dfs2(p);
        ans[x] += ans[p];
        q = pre[q];
    }
    ans[x] += size[x];
}

int main(){
    int i;

    scanf("%d", &n);
    for (i = 1; i <= n; i++) {
        scanf("%d", &link[i]);
        connect(i, link[i]);
    }
    for (i = 1; i <= n; i++) 
        if (dfn[i] == 0) dfs(i);
    //
    for (i = 1; i <= n; i++)
        if (belong[i] != belong[link[i]]) connect(belong[i], belong[link[i]]);
    //
    for (i = 1; i <= n; i++)
        if (ans[belong[i]] == 0) dfs2(belong[i]);
    //
    for (i = 1; i <= n; i++) printf("%d\n", ans[belong[i]]);
    return 0;
}

                                                                        ——by Eirlys

 

转载请注明出处=w=

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值