【洛谷P2597】灾难

题目

题目链接:https://www.luogu.com.cn/problem/P2597
一个食物网有 \(n\) 个点,代表 \(n\) 种生物,生物从 \(1\)\(n\) 编号。
如果生物 \(x\) 可以吃生物 \(y\),那么从 \(y\)\(x\) 连一个有向边。
这个图没有环。
图中有一些点没有连出边,这些点代表的生物都是生产者,可以通过光合作用来生存。
而有连出边的点代表的都是消费者,它们必须通过吃其他生物来生存。
如果某个消费者的所有食物都灭绝了,它会跟着灭绝。
我们定义一个生物在食物网中的“灾难值”为,如果它突然灭绝,那么会跟着一起灭绝的生物的种数。
给定一个食物网,你要求出每个生物的灾难值。
\(n\leq 65534,m\leq 10^6\)

思路

如果 \(x\)\(y\),那么就从 \(y\)\(x\) 连边。这样我们得到了一张 DAG。每一个点的答案就是直接或间接连向他的点的数量。
建立一个虚根 \(S\),从 \(S\) 向度数为 \(0\) 的点连边。然后拓扑排序。每次取出队首 \(u\) 后,用倍增 LCA 求它的祖先,然后枚举 \(u\) 连向的点 \(v\),容易发现 \(v\) 应该被它被连向的点的 LCA 支配。然后不断更新 \(v\) 的父亲节点即可。
最后建出支配树,答案就是每一个点的子树大小了。
时间复杂度 \(O(n\log n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=70010,M=1000010,LG=18;
int n,S;

struct edge
{
	int next,to;
};

struct Tree
{
	int tot,head[N],dep[N],f[N][LG+1],siz[N];
	edge e[N];
	
	void add(int from,int to)
	{
		e[++tot]=(edge){head[from],to};
		head[from]=tot;
	}
	
	int lca(int x,int y)
	{
		if (dep[x]<dep[y]) swap(x,y);
		for (int i=LG;i>=0;i--)
			if (dep[f[x][i]]>=dep[y]) x=f[x][i];
		if (x==y) return x;
		for (int i=LG;i>=0;i--)
			if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
		return f[x][0];
	}
	
	void dfs(int x)
	{
		siz[x]=1;
		for (int i=head[x];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (v!=f[x][0])
			{
				dfs(v);
				siz[x]+=siz[v];
			}
		}
	}
}T;

struct Graph
{
	int tot,head[N],deg[N];
	edge e[M+N];
	
	void add(int from,int to)
	{
		e[++tot]=(edge){head[from],to};
		head[from]=tot; deg[to]++;
	}
	
	void topsort()
	{
		memset(T.f,-1,sizeof(T.f));
		queue<int> q;
		for (int i=1;i<=n;i++)
			if (!deg[i]) add(S,i);
		q.push(S); T.f[S][0]=0;
		while (q.size())
		{
			int u=q.front(); q.pop();
			T.dep[u]=T.dep[T.f[u][0]]+1;
			for (int i=1;i<=LG;i++)
				T.f[u][i]=T.f[T.f[u][i-1]][i-1];
			for (int i=head[u];~i;i=e[i].next)
			{
				int v=e[i].to;
				if (T.f[v][0]==-1) T.f[v][0]=u;
					else T.f[v][0]=T.lca(u,T.f[v][0]);
				deg[v]--;
				if (!deg[v]) q.push(v);
			}
		}
	}
}G;

int main()
{
	memset(G.head,-1,sizeof(G.head));
	memset(T.head,-1,sizeof(T.head));
	scanf("%d",&n);
	for (int i=1,x;i<=n;i++)
		while (scanf("%d",&x) && x) G.add(x,i);
	S=n+1;
	G.topsort();
	for (int i=1;i<=n;i++)
		T.add(T.f[i][0],i);
	T.dfs(S);
	for (int i=1;i<=n;i++)
		printf("%d\n",T.siz[i]-1);
	return 0;
}
posted @ 2021-01-16 11:47  stoorz  阅读(95)  评论(0编辑  收藏  举报