Live2D

Solution -「ZJOI2012」「洛谷 P2597」灾难

Description

  link.
  给定一个捕食网络,对于每个物种,求其灭绝后有多少消费者失去所有食物来源。(一些名词与生物学的定义相同 w。)
  原图结点数 n65534,边数 m106,图保证无有向环。

Solution

  支配树板题。将原图反向建边,令一个“超级生产者”结点,指向所有生产者,然后求出该图的支配树。每个物种的答案就是其子树大小 1
  以下会讲解对于有向无环图(DAG),如何建出支配树。

在 DAG 上建支配树

  声明对“支配”的定义:对于 u 的支配点 v,满足删去 v 后,从源点不可到达 u
  最近支配点:所有支配 uv 中距离 u 最近的一个点。可以证明某个点的最近支配点唯一。
  按照拓扑序,从源点开始处理。可以直观地发现,点 u 的最近支配点就是其所有前驱结点在支配树上的 LCA。所以只需要每次向支配树加入当前结点,并处理处当前结点的倍增信息即可。

Code

#include <queue>
#include <cstdio>
#include <vector>

#define adj( g, u, v ) \
	for ( int _eid = g.head[u], v; v = g.to[_eid], _eid; _eid = g.nxt[_eid] )

const int MAXN = 65534, MAXM = 1e6, MAXLG = 15;
int n, dep[MAXN + 5], ans[MAXN + 5], rnk[MAXN + 5], fa[MAXN + 5][MAXLG + 5];
std::queue<int> que;
std::vector<int> pre[MAXN + 5];

inline int rint () {
	int x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x;
}

struct Graph {
	int ecnt, head[MAXN + 5], to[MAXM + 5], nxt[MAXM + 5], ind[MAXN + 5];
	inline void link ( const int s, const int t ) {
		++ ind[to[++ ecnt] = t], nxt[ecnt] = head[s], head[s] = ecnt;
		pre[t].push_back ( s );
	}
} sour, domt;

inline int LCA ( int u, int v ) {
	if ( dep[u] < dep[v] ) u ^= v ^= u ^= v;
	for ( int i = 15; ~ i; -- i ) if ( dep[fa[u][i]] >= dep[v] ) u = fa[u][i];
	if ( u == v ) return u;
	for ( int i = 15; ~ i; -- i ) if ( fa[u][i] ^ fa[v][i] ) u = fa[u][i], v = fa[v][i];
	return fa[u][0];
}

inline void calc ( const int u ) {
	ans[u] = 1;
	adj ( domt, u, v ) calc ( v ), ans[u] += ans[v];
}

int main () {
	n = rint ();
	for ( int i = 1, t; i <= n; ++ i ) while ( t = rint () ) sour.link ( t, i );
	for ( int i = 1; i <= n; ++ i ) if ( ! sour.ind[i] ) sour.link ( n + 1, i );
	int cnt = 0;
	que.push ( n + 1 );
	for ( int u; ! que.empty (); que.pop () ) {
		rnk[++ cnt] = u = que.front ();
		adj ( sour, u, v ) if ( ! -- sour.ind[v] ) que.push ( v );
	}
	for ( int i = 1; i <= cnt; ++ i ) {
		int u = rnk[i], f = 0;
		if ( ! pre[u].empty () ) f = pre[u][0];
		for ( int j = 1; j < ( int ) pre[u].size (); ++ j ) f = LCA ( f, pre[u][j] );
		dep[u] = dep[fa[u][0] = f] + 1, domt.link ( f, u );
		for ( int j = 1; j <= 15; ++ j ) fa[u][j] = fa[fa[u][j - 1]][j - 1];
	}
	calc ( n + 1 );
	for ( int i = 1; i <= n; ++ i ) printf ( "%d\n", ans[i] - 1 );
	return 0;
}
posted @   Rainybunny  阅读(108)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示