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

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

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

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

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

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

Solution

看到环直接想到 \(Tarjan\)
因为每个点出度都为 \(1\), 所以此图只能构成简单环
对于询问的每个点, 分两种情况讨论:

  1. 此点在环内: 显然答案为环的大小
  2. 此点不在环内: 答案为顺着路径走到最近环经过的点数 + 最近环的大小

在环内很好确认, 当此点所在的颜色块包含点数大于等于 \(2\) 个则为环
有一种特殊情况为自环, 此题中出边只有一条, 判断一下出边连向自己即可

再环外的话通过 \(dfs\) 遍历图 , 直到找到环内节点即可
用记忆化减少遍历量

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 100019,INF = 1e9 + 19;
int head[maxn],nume = 1;
struct Node{
    int v,dis,nxt;
    }E[maxn << 3];
void add(int u,int v,int dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
int num;
int DFN[maxn], LOW[maxn], Index;
int S[maxn], top;
bool ins[maxn];
int numc, col[maxn], nc[maxn];
void Tarjan(int u){
	DFN[u] = LOW[u] = ++Index;
	S[++top] = u;ins[u] = 1;
	for(int i = head[u];i;i = E[i].nxt){
		int v = E[i].v;
		if(!DFN[v])Tarjan(v), LOW[u] = min(LOW[u], LOW[v]);
		else if(ins[v])LOW[u] = min(LOW[u], DFN[v]);
		}
	if(DFN[u] == LOW[u]){
		numc++;
		while(S[top + 1] != u){
			col[S[top]] = numc;
			nc[numc]++;
			ins[top--] = 0;
			}
		}
	}
int dp[maxn];
int DP(int u){
	if(dp[u] != 0)return dp[u];//记忆化
	if(nc[col[u]] > 1){dp[u] = nc[col[u]];return dp[u];}//环内
	for(int i = head[u];i;i = E[i].nxt){
		int v = E[i].v;
		if(v == u){dp[u] = 1;return dp[u];}//自环
		dp[u] = DP(v) + 1;//链上
		}
	return dp[u];
	}
int main(){
	num = RD();
	REP(i, 1, num){
		int v = RD();
		add(i, v, 1);
		}
	REP(i, 1, num)if(!DFN[i])Tarjan(i);
	REP(i, 1, num){
		printf("%d\n", DP(i));
		}
	return 0;
	}
posted @ 2018-10-16 21:11  Tony_Double_Sky  阅读(123)  评论(0编辑  收藏  举报