[BZOJ2815][ZJOI2012]灾难(拓扑排序/支配树)
支配树目前只见到这一个应用,那就不独分一类,直接作为拓扑排序题好了。
每个点向所有食物连边,定义fa[x]为x的支配点,即离x最近的点,满足若fa[x]灭绝,则x也要灭绝。
这样,将fa[x]向x连边,则建出的新图是一棵树,这就是支配树(不是严谨的支配树,被出题人称为灭绝树)
建树流程是,将拓扑序反向,对于一个点x,求它的出点在新图(树)中的LCA,然后将这个LCA作为fa[x]即可。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 7 typedef long long ll; 8 using namespace std; 9 10 const int N=100010; 11 int n,x,sz[N],dep[N],d[N],fa[N][18],q[N]; 12 13 int lca(int u,int v){ 14 if (u==-1) return v; 15 if (dep[u]<dep[v]) swap(u,v); 16 int t=dep[u]-dep[v]; 17 for (int i=16; ~i; i--) if (t&(1<<i)) u=fa[u][i]; 18 if (u==v) return u; 19 for (int i=16; ~i; i--) if (fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; 20 return fa[u][0]; 21 } 22 23 struct Graph{ 24 int cnt,h[N],to[N<<1],nxt[N<<1]; 25 26 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 27 28 void Top(){ 29 int st=0,ed=0; 30 rep(i,1,n) if (!d[i]) q[++ed]=i; 31 while (st<ed){ 32 int x=q[++st]; 33 For(i,x){ d[k=to[i]]--; if (!d[k]) q[++ed]=k; } 34 } 35 } 36 37 void ext(int x,int y){ 38 add(x,y); dep[y]=dep[x]+1; fa[y][0]=x; 39 rep(i,1,16) fa[y][i]=fa[fa[y][i-1]][i-1]; 40 } 41 42 void dfs(int x){ sz[x]=1; For(i,x) dfs(k=to[i]),sz[x]+=sz[k]; } 43 }G1,G2; 44 45 void build(){ 46 for (int i=n; i; i--){ 47 int x=q[i],tmp=-1; 48 for (int i=G1.h[x]; i; i=G1.nxt[i]) tmp=lca(tmp,G1.to[i]); 49 G2.ext(max(tmp,0),x); 50 } 51 } 52 53 int main(){ 54 freopen("bzoj2815.in","r",stdin); 55 freopen("bzoj2815.out","w",stdout); 56 scanf("%d",&n); 57 rep(i,1,n){ 58 for (scanf("%d",&x); x; scanf("%d",&x)) G1.add(i,x),d[x]++; 59 } 60 G1.Top(); build(); G2.dfs(0); 61 rep(i,1,n) printf("%d\n",sz[i]-1); 62 return 0; 63 }