[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);
}

浙公网安备 33010602011771号