P2597 [ZJOI2012]:灾难(支配树)

3 篇文章 0 订阅
2 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
题解:根据定义,若不存在生产者到x的路径,那么x就会灭绝,一个物种的灾难值:将这个点抠掉,有多少个点无法从生产者到达。对输入反向建图,构建支配树,物种的灾难值就是该点在支配树上的子节点个数(不包括自己)。

由于图是DAG,可以按拓扑序构建支配树:按拓扑序遍历所有点,对于当前处理的点x,[1,x - 1]已经构成了支配树,求出所有能到达它的点在支配树上的lca,lca 就是 x的支配点。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
const int mx = 17;
int n;
vector<int> g[maxn],rg[maxn],h[maxn],tmp;
int ru[maxn],f[maxn][mx + 1],dep[maxn],son[maxn];
void init() {
	memset(ru,0,sizeof ru);
	memset(f,0,sizeof f);
	memset(dep,0,sizeof dep);
}
void tpsort() {
	queue<int> q;
	for(int i = 1; i <= n; i++)
		if(!ru[i]) q.push(i);
	while(!q.empty()) {
		int top = q.front();
		q.pop();
		tmp.push_back(top);
		for(int i = 0; i < g[top].size(); i++) {
			int v = g[top][i];
			ru[v]--;
			if(!ru[v]) q.push(v);
		}
	}
}
int lca(int u,int v) {
	if(dep[u] < dep[v]) swap(u,v);
	for(int i = mx; i >= 0; i--)
		if(dep[f[u][i]] >= dep[v]) u = f[u][i];
	if(u == v) return u;
	for(int i = mx; i >= 0; i--)
		if(f[u][i] != f[v][i]) {
			u = f[u][i];
			v = f[v][i];
		}
	return f[u][0];
}
void dfs(int u,int fa) {
	son[u] = 1;
	for(auto it : h[u]) {
		if(it == fa) continue;
		dfs(it,u);
		son[u] += son[it];
	}
}
int main() {
	init();
	scanf("%d",&n);
	int x;
	for(int i = 1; i <= n; i++) {
		while(scanf("%d",&x) && x) {
			g[x].push_back(i);
			ru[i]++;
			rg[i].push_back(x);
		}
	}
	tpsort();
	for(auto it : tmp) {
		if(rg[it].size() == 0) {
			h[0].push_back(it);
			dep[it] = 1;
			continue;
		}
		int p = rg[it][0];
		for(int j = 1; j < rg[it].size(); j++)
			p = lca(p,rg[it][j]);
		h[p].push_back(it);
		f[it][0] = p;dep[it] = dep[p] + 1;
		for(int i = 1; i <= mx; i++) f[it][i] = f[f[it][i - 1]][i - 1];
	}
	dfs(0,0);
	for(int i = 1; i <= n; i++) {
		printf("%d\n",son[i] - 1);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值