[Luogu P2597] [BZOJ 2815] [ZJOI2012]灾难

8 篇文章 0 订阅
1 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

阿米巴是小强的好朋友。

阿米巴和小强在草原上捉蚂蚱。小强突然想,如果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难。

学过生物的阿米巴告诉小强,草原是一个极其稳定的生态系统。如果蚂蚱灭绝了,小鸟照样可以吃别的虫子,所以一个物种的灭绝并不一定会引发重大的灾难。

我们现在从专业一点的角度来看这个问题。我们用一种叫做食物网的有向图来描述生物之间的关系:

一个食物网有 N N N个点,代表 N N N种生物,如果生物 x x x可以吃生物 y y y,那么从 y y y x x x连一个有向边。

这个图没有环。

图中有一些点没有连出边,这些点代表的生物都是生产者,可以通过光合作用来生存; 而有连出边的点代表的都是消费者,它们必须通过吃其他生物来生存。

如果某个消费者的所有食物都灭绝了,它会跟着灭绝。

我们定义一个生物在食物网中的“灾难值”为,如果它突然灭绝,那么会跟着一起灭绝的生物的种数。

举个例子:在一个草场上,生物之间的关系是:

img

如果小强和阿米巴把草原上所有的羊都给吓死了,那么狼会因为没有食物而灭绝,而小强和阿米巴可以通过吃牛、牛可以通过吃草来生存下去。所以,羊的灾难值是 1 1 1。但是,如果草突然灭绝,那么整个草原上的5种生物都无法幸免,所以,草的灾难值是 4 4 4

给定一个食物网,你要求出每个生物的灾难值。

输入输出格式

输入格式:

输入文件 catas.in 的第一行是一个正整数 N N N,表示生物的种数。生物从 1 1 1 标号到 N N N

接下来 N N N 行,每行描述了一个生物可以吃的其他生物的列表,格式为用空格隔开的若干个数字,每个数字表示一种生物的标号,最后一个数字是 0 0 0 表示列表的结束。

输出格式:

输出文件catas.out包含 N N N行,每行一个整数,表示每个生物的灾难值。

输入输出样例

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

说明

【样例说明】

样例输入描述了题目描述中举的例子。

【数据规模】

50 % 50\% 50%的数据, N ≤ 10000 N ≤ 10000 N10000

100 % 100\% 100%的数据, 1 ≤ N ≤ 65534 1 ≤ N ≤ 65534 1N65534

输入文件的大小不超过1M。保证输入的食物网没有环。

解题分析

很有意思的一道题。

考虑建立一棵树, 首先有一个虚拟节点, 令所有的生产者是这个点的儿子。

我们发现, 对于一个消费者, 其食物不一定会导致它死亡, 我们需要找到一个深度尽量浅的点, 满足这个点是所有其食物的祖先节点, 并且无法通过其他点到达这个点的祖先节点。 这样的一个点或其必经的祖先的死亡才会对这个消费者产生致死的效果。

然后似乎这个点就是在我们建立的树上所有食物的 L C A LCA LCA, 把这个消费者挂在 L C A LCA LCA的下面就好了。最后统计答案的时候直接 D F S DFS DFS整颗树取子树 s i z − 1 siz-1 siz1即可。

然后 L C A LCA LCA动态维护倍增数组就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <queue>
#include <cassert>
#include <vector>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int n;
bool vis[MX];
int fat[17][MX];
int deg[MX], siz[MX], dep[MX];
std::queue <int> q;
std::vector <int> g[MX], t[MX], fod[MX];
IN int LCA(R int x, R int y)
{
	if (dep[x] < dep[y]) std::swap(x, y);
	int del = dep[x] - dep[y], tim = 0;
	W (del)
	{
		if (del & 1) x = fat[tim][x];
		++tim, del >>= 1;
	}
	if (x == y) return x;
	for (tim = 16; ~tim; --tim)
	{
		if (fat[tim][x] ^ fat[tim][y])
		x = fat[tim][x], y = fat[tim][y];
	}
	return fat[0][x];
}
void DFS(R int now)
{
	siz[now] = 1;
	for (auto i : t[now]) DFS(i), siz[now] += siz[i];
}
int main(void)
{
	in(n); int foo, bar;
	for (R int i = 1; i <= n; ++i)
	{
		W (233)
		{
			in(foo); if (!foo) break;
			fod[i].push_back(foo);
			g[foo].push_back(i);
			++deg[i];
		}
	}
	for (R int i = 1; i <= n; ++i) if ((!deg[i]) && (!vis[i]))
	{
		dep[i] = 1; vis[i] = true;
		fat[0][i] = n + 1; t[n + 1].push_back(i);
		for (auto j : g[i]) if ((!(--deg[j])) && (!vis[j])) vis[j] = true, q.push(j);
	}
	int tcnt = 0;
	W (!q.empty())
	{
		++tcnt;
		foo = q.front(); q.pop();
		bar = fod[foo][0];
		for (auto j : fod[foo])
		bar = LCA(bar, j);
		dep[foo] = dep[bar] + 1; fat[0][foo] = bar;
		t[bar].push_back(foo);
		for (R int i = 1; i <= 16; ++i)
		{
			fat[i][foo] = fat[i - 1][fat[i - 1][foo]];
			if (!fat[i][foo]) break;
		}
		for (auto j : g[foo]) if (!(--deg[j])) q.push(j);
	}
	DFS(n + 1);
	for (R int i = 1; i <= n; ++i) printf("%d\n", siz[i] - 1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值