BZOJ2815&&洛谷P2597 [ZJOI2012]灾难

xiaoxiao2025-05-27  26

拓扑+LCA

思路清奇的一道好题

这个题真的不毒瘤!!

吐槽一下出题人如何同时吓死草原上的羊??

先考虑一下暴力

反向建图跑个拓扑就完事了

考虑一些特殊的情况

假如输入的是一棵树,那么一个点的贡献就是他的子树大小-1,也就是子树中除他以外的所有点,但是大多数情况输入是一个DAG,那我们是不是可以找到一种方法,把它转化成一棵树呢? 我们考虑样例的情况 不难发现,若是4号点凉了,当且仅当2,3都凉了,而2,3都凉了的情况得建立在1凉了的情况下,也就是说4只会对1凉了这种情况产生贡献,所以我们就可以直接把4挂到1下面,这样就不用管2,3对4的影响了,然后我们考虑 ,对于1个点指向的多个点,1会产生贡献,只有在它的食物,也就是他指向的这一堆点全部凉了的情况下,而这一堆点都凉了,那必然他们的食物也凉了,一直向上推,直到他们只有一个食物,也就是说所有点交在了一起时,这个交点凉了,那么下面也就都凉了,这个点是啥?

是下面所有点在新树上的LCA,但发现有些点没有食物?我们额外建一个0号点,使得所有没没有食物的点的fa=0,这样就会交于一点了 ,我们在原图上跑拓扑,按拓扑序的倒序开始建树,因为他的拓扑序越靠后,那么他在新树上的深度就越浅,因为他被最多的人指了,必然在食物链最底端,然后我们倒序遍历就相当于正序建树了,我们把一个点接到他指向的所有点的LCA上就好了,而他指向的所有点,必然比他先处理了,最后我们dfs求一遍子树大小就好了

代码

//By AcerMo #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int M=100500; int n; int to[M],nxt[M],head[M],cnt; int net[M],go[M],last[M],tot; int f[M][20],dep[M],ti[M],in[M],ans[M]; inline void read(int &x) { x=0;char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return ; } inline void ad1(int x,int y) { to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt; return ; } inline void ad2(int x,int y) { go[++tot]=y;net[tot]=last[x];last[x]=tot; return ; } inline void topsort() { queue<int>q;int t=0; for (int i=1;i<=n;i++) if (!in[i]) q.push(i); while (q.size()) { int u=q.front();q.pop();ti[++t]=u; for (int i=head[u];i;i=nxt[i]) if (--in[to[i]]==0) q.push(to[i]); } return ; } inline int lca(int x,int y) { if (dep[x]<dep[y]) swap(x,y); for (int i=18;i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][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]; } inline void built() { dep[0]=1; memset(last,-1,sizeof(last)); for (int i=n;i>=1;i--) { int u=ti[i]; if (head[u]==0) {ad2(0,u);dep[u]=2;continue;} int l=to[head[u]]; for (int k=nxt[head[u]];k;k=nxt[k]) l=lca(l,to[k]);ad2(l,u); dep[u]=dep[l]+1;f[u][0]=l; for (int k=1;k<=18;k++) f[u][k]=f[f[u][k-1]][k-1]; } return ; } inline void dfs(int x) { ans[x]=1; for (int i=last[x];~i;i=net[i]) dfs(go[i]),ans[x]+=ans[go[i]]; return ; } signed main() { read(n);int x; for (int i=1;i<=n;i++) while (1) { read(x);if (!x) break; in[x]++;ad1(i,x); } topsort();built();dfs(0); for (int i=1;i<=n;i++) printf("%d\n",ans[i]-1); return 0; }
转载请注明原文地址: https://www.6miu.com/read-5030808.html

最新回复(0)