1. 程式人生 > >[BZOJ2815][ZJOI2012]災難(拓撲排序/支配樹)

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