[ZJOI2012]灾难

\(\text{Solution}\)

一道简单的图论,考虑捕食关系
对于每一个点,找到第一个能影响到自己的点,以这种方式建出一颗树,答案就是子树大小减一。
如何实现呢?
按拓扑序遍历,动态的维护一个\(LCA\),求的就是能被自己吃的点的\(LCA\)

\(\text{Code}\)

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 7e4;
int h[N],h1[N],n,nh[N],tot,tot1,ntot,f[N][20],dep[N],siz[N],in[N],q[N];

struct edge{
	int to,nxt;
}e[N * 20],e1[N * 20],ne[N * 20];
void add(int x,int y){e[++tot] = edge{y,h[x]},h[x] = tot;}
void add1(int x,int y){e1[++tot1] = edge{y,h1[x]},h1[x] = tot1;}
void addnew(int x,int y)
{
	ne[++ntot] = edge{y,nh[x]},nh[x] = ntot;
	f[y][0] = x,dep[y] = dep[x] + 1;
	for (int i = 1; i <= 18; i++)
		if (f[y][i - 1]) f[y][i] = f[f[y][i - 1]][i - 1];
		else break;
}
int lca(int x,int y)
{
	if (!x) return y;
	if (dep[x] > dep[y]) swap(x,y);
	int tmp = dep[y] - dep[x];
	for (int i = 0; tmp; tmp >>= 1,i++)
		if (tmp & 1) y = f[y][i];
	if (x == y) return x;
	for (int i = 18; 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 u)
{
	siz[u] = 1;
	for (int i = nh[u]; i; i = ne[i].nxt) 
		dfs(ne[i].to),siz[u] += siz[ne[i].to];
}
int main()
{
	scanf("%d",&n);
	for (int i = 1,q; i <= n; i++)
	{
		scanf("%d",&q);
		for (; q; scanf("%d",&q)) add(q,i),in[i]++,add1(i,q);  
	}
	int head = 0,tail = 0; dep[n + 1] = 1;
	for (int i = 1; i <= n; i++)
		if (!in[i]) add(n + 1,i),in[i]++,q[++tail] = i,add1(i,n + 1);
	while (head < tail)
	{
		int u = q[++head],g = 0;
		for (int i = h1[u]; i; i = e1[i].nxt) g = lca(g,e1[i].to);
		addnew(g,u);
		for (int i = h[u]; i; i = e[i].nxt)
		{
			int v = e[i].to; in[v]--;
			if (!in[v]) q[++tail] = v;
		}
	}
	dfs(n + 1);
	for (int i = 1; i <= n; i++) printf("%d\n",siz[i] - 1);
}
posted @ 2022-04-13 12:10  RiverSheep  阅读(35)  评论(0)    收藏  举报